Skip to main content

Operations

Logs and health​

docker compose logs -f app # application
docker compose logs -f worker # background dispatcher
docker compose ps # status + healthcheck

The image defines a healthcheck on GET /login; wire it into your monitor. Inject request ids/correlation as your log pipeline requires.

Migrations​

Migrations live in drizzle/ and run automatically via the one-shot migrate service on every docker compose up. To apply them explicitly:

docker compose run --rm migrate pnpm db:migrate

Apply migrations before rolling out new app code. Migrations must be backward compatible for the duration of a rolling deploy.

Upgrades​

git pull
docker compose build --pull
docker compose run --rm migrate pnpm db:migrate # or rely on `up`
docker compose up -d

ORIGIN is optional and no longer baked, so changing the public URL needs no rebuild — set ORIGIN/PUBLIC_ORIGIN to pin it, or rely on the request-derived origin (see Production hardening).

Releases​

Tags matching v*.*.* run .github/workflows/publish-image.yml, which builds the single image and pushes it to GHCR (ghcr.io/<owner>/<repo>) with tags X.Y.Z, latest, and a sha-… handle. ORIGIN is optional and not baked, so one image serves any domain; set PUBLIC_ORIGIN only to pin a canonical origin.

To deploy a release, pull the pinned tag, run migrations, then roll the app and worker to the same tag:

docker pull ghcr.io/<owner>/<repo>:X.Y.Z
docker run --rm -e DATABASE_URL='…' ghcr.io/<owner>/<repo>:X.Y.Z pnpm db:migrate
# then run the app/worker from ghcr.io/<owner>/<repo>:X.Y.Z

Backups​

The bundled database stores data in the pgdata volume.

# logical dump of the bundled database
docker compose exec -T db pg_dump -U intranet intranet > intranet-$(date +%F).sql

Restore:

cat intranet-2026-10-08.sql | docker compose exec -T db psql -U intranet -d intranet

For managed Postgres, use the provider's automated backups and PITR.

tip

Also export the .env (secrets) alongside backups — the database alone is not a full restore.

The worker and the outbox​

app writes events to a transactional outbox table; the worker dispatches them (at-least-once). Run exactly one worker per environment unless you have confirmed idempotent handlers. If the outbox backs up, check docker compose logs worker and confirm the worker can reach the database and any downstream services.

Seeding​

docker compose run --rm migrate pnpm db:seed # idempotent
docker compose run --rm migrate pnpm db:seed -- --reset # clear domain data first

--reset clears domain and credential tables but keeps user, session, and account rows.

Inspecting the database​

docker compose exec db psql -U intranet -d intranet

Clock and timezone​

Durations are stored in integer seconds and timestamps in UTC. Run containers on a host with a correct, synchronised clock (NTP); the worker's scheduling depends on it.

Rotation​

SecretEffect of rotation
BETTER_AUTH_SECRETInvalidates sessions — users sign in again.
OAUTH_CLIENT_SECRETRecreate the client in Authentik and update .env.
API tokens / service clientsRecreate in the admin UI; old secrets stop working.