Backups & upgrades
All of your business data lives in Postgres - clients, projects, time, invoices, expenses, logos and receipts included. That means backups and upgrades are ordinary Postgres and Docker operations, with nothing Clerq-specific to learn.
The examples assume the bundled docker-compose.yml, whose database service
is named postgres and whose default role and database are both clerq.
Adjust the names if you overrode POSTGRES_USER or POSTGRES_DB.
Back up
Dump the database on a schedule. A nightly cron is plenty for most setups:
docker compose exec -T postgres \
pg_dump -U clerq clerq | gzip > clerq-$(date +%F).sql.gz
Store the dumps off the host - object storage or another machine - so a single disk failure cannot take both the instance and its backups. Test a restore into a throwaway database now and then; an untested backup is a guess.
Restore
Restore a dump into a running, empty database:
gunzip -c clerq-2026-06-14.sql.gz | \
docker compose exec -T postgres psql -U clerq clerq
To restore onto a fresh instance, bring the stack up once so the database and schema exist, then load your dump over it.
Upgrade
Clerq is run from source, so an upgrade is a git pull and a rebuild:
# 1. take a fresh backup first (see above)
# 2. get the new code
git pull
# 3. rebuild and restart
docker compose up -d --build
On startup the one-shot migrate service runs any new migrations before the
app container starts, so the schema is always brought up to date for you.
Migrations are forward-only.
Always take a fresh backup immediately before upgrading a production instance, and read the release notes for any manual steps.
Health checks
The app exposes a public health endpoint over tRPC that returns JSON without authentication - useful for uptime monitors and container health checks:
curl http://localhost:3000/api/trpc/health.ping
# {"result":{"data":{"json":{"ok":true,"time":"..."}}}}
A plain TCP or HTTP check against the app port works too. The database has its
own pg_isready health check built into the compose file.
Troubleshooting
The app container exits or restarts immediately. Almost always a missing
or wrong DATABASE_URL, or the database not being ready. Check the logs:
docker compose logs app
docker compose logs migrate
docker compose logs postgres
"DATABASE_URL is not set." The app could not find a connection string.
Under compose this is built from the POSTGRES_* variables - make sure your
.env is next to the compose file and the values are set.
"BETTER_AUTH_SECRET is not set; cannot sign invoice PDF links." Set a
BETTER_AUTH_SECRET. See
Environment variables.
A low-entropy secret warning in the logs. Your BETTER_AUTH_SECRET is too
short or too predictable. Generate a real one with openssl rand -base64 32
and restart. Note that changing it signs everyone out.
Sign-in redirects fail or loop after going public. BETTER_AUTH_URL does
not match the URL users actually reach the instance on. Set it to the exact
external origin, including https://, and restart.
Migrations did not apply. Re-run the one-shot step explicitly:
docker compose run --rm migrate