Perennia docs

Self-host

Perennia keeps hosted and self-host deployments close together: self-hosting runs the same Docker image used by the hosted service with standard Postgres as the only required stateful dependency.

Runtime shape

A production-shaped self-host install has:

  • One API web service running the compiled server entrypoint.
  • One Background Worker running node dist/worker.js.
  • One Postgres database shared by the API and worker through DATABASE_URL.

The API exposes /healthz for liveness and /readyz for Postgres readiness. Run the worker separately from the API process so background jobs, purge windows, nudges, and backup aging keep moving even when web traffic is quiet.

Database and jobs

Apply Drizzle Kit migrations on container start before serving traffic. The worker uses the same migration path, then lets pg-boss create or upgrade its own pgboss schema.

No Redis is required for 1.0. pg-boss is the Postgres-backed queue and cron spine for jobs, push fan-out, and scheduled maintenance. Rate limiting is Postgres-backed, and analytics are computed on demand rather than cached in Redis.

Render reference

The managed 1.0 hosting path runs on Render in a single US region, with Oregon as the blueprint default. Render provides container-native web services, paired Background Workers, and managed Postgres with backups and point-in-time recovery.

That Render setup is a reference deployment, not a proprietary requirement. The lock-in boundary is intentionally small: a portable container plus standard Postgres can move to a VPS or another container host later.

Required configuration

At minimum, provide the database connection, auth, email, and OAuth settings used by the server. Crash reporting remains opt-in and default-off; enable it only by setting SERVER_CRASH_REPORTING_ENABLED=true with a SERVER_CRASH_REPORTING_DSN for a Sentry or GlitchTip-compatible backend.

Before exposing a self-hosted service, verify:

  • The API starts after migrations finish.
  • GET /healthz returns 200.
  • GET /readyz returns 200 with database status ok.
  • Worker logs show pg-boss started and the job spine heartbeat.
  • Structured logs are scrubbed of training data, body-measurement data, comments, and PII beyond user id.