Upgrades
Five-step upgrade: backup Postgres, pull the GHCR image, confirm health, roll back with the dump.
The image is ghcr.io/epure-sh/epure. There is no worker fleet or Redis stream to drain. Path: backup → pull → watch health. Keep .env; Compose still interpolates it.
Upgrade in five steps
Dump Postgres, note the commit you are leaving, check out the new revision, compose up (pulls GHCR), confirm /health.
Pin production
Default compose uses ghcr.io/epure-sh/epure:latest. In production set EPURE_IMAGE=ghcr.io/epure-sh/epure:vX.Y.Z in .env.
Dump Postgres
docker compose exec -T postgres pg_dump -U epure -d epure -Fc > "epure-$(date +%Y%m%d).dump"Store the dump off-box. Prod overlay: same exec; Postgres has no host port.
Optional artifacts (if you upload maps):
Archive source-map volume
command
docker volume ls docker run --rm \ -v epure_artifacts_data:/data \ -v "$(pwd)/backups:/backup" \ alpine tar czf /backup/artifacts-$(date +%Y%m%d).tar.gz -C /data .
Note the git commit you are leaving.
Check out the new revision
git fetch
git checkout <commit>Pull and start
Pull local stack
command
docker compose up -d
Migrations apply when the epure process starts. There is no separate migrate container.
Confirm health
docker compose logs -f EpureCheck health
Local default. Use EPURE_PUBLIC_URL/health if you changed the port or are on HTTPS.
command
curl -sS http://localhost:8080/health
Expected
{"status":"ok"}On production, use EPURE_PUBLIC_URL/health. Local default is http://localhost:8080/health. Then: login, Issues list, one store POST → 202. Quickstart Try ingest.
Roll back if it fails
Schema may move forward only. Restore the dump before starting an older binary if a migration is not backward-compatible.
Restore dump and previous binary
Keep Postgres up. Stop only the app, restore, then compose up the previous image.
command
docker compose stop Epure git checkout <previous-commit> docker compose exec -T postgres pg_restore -U epure -d epure --clean --if-exists < epure-YYYYMMDD.dump docker compose up -d curl -sS http://localhost:8080/health
Expected
{"status":"ok"}--clean drops objects before restore. Confirm /health and login. Keep EPURE_SESSION_SECURE matched to HTTP vs HTTPS.
What stays the same
No upgrade introduces Redis or a worker fleet.
- Two-container topology (app + Postgres 16)
EPURE_MODE=all- RLS session variable:
app.current_org_id - Volume names for Postgres and artifacts (unless you rename the Compose project)
Checklist
- Dump taken and stored off-box
- Artifacts archived if needed
-
docker compose … up -dcompleted without migrate errors -
/healthOK - Session still works
- Ingest smoke test passed
What if migrate errors show in Epure logs?
Stop retrying a half-applied schema. Restore the dump, check out the previous commit, compose up. Migrations run when the epure process starts, so there is no separate migrate container. Keep the dump off-box: Troubleshooting.
Why does login loop after an upgrade?
Cookie Secure flag mismatch. The prod overlay sets EPURE_SESSION_SECURE=1; local HTTP needs 0. Keep that value matched to HTTP vs HTTPS when you roll back, then confirm /health. See Configuration.
Can I roll back the schema with an older binary?
Only if you restore the pre-upgrade dump first, since schema moves forward only. --clean on pg_restore drops objects before restore. Confirm /health and login after compose up.
Do I drain Redis or workers?
No. Two containers, with no Redis stream and no worker fleet. Stop epure, restore Postgres if needed, compose up. Artifacts live on the artifacts_data volume if you uploaded maps.