scripts/assert-deploy.ts polls /api/version on prod + demo and asserts the served data.version matches an expected value, printing each target's buildSha and exiting non-zero on a mismatch or after a bounded retry loop. This catches the BuildKit layer-cache pitfall where the deploy reports clean but /api/version still answers the prior release. Run via `pnpm dlx tsx scripts/assert-deploy.ts <version>` — not bundled into the standalone image. Document it in the deploy runbook. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit a86a9151ef670846c4b33a1c068da40324393308)
3.1 KiB
Deploy ordering
Coolify handles the standard deploy ordering automatically: the
container image is pulled, docker-entrypoint.sh runs
prisma migrate deploy against the live database, and only then does
the Next.js process bind to the listener. This page documents the
ordering explicitly for manual-rollback and partial-deploy scenarios
where the operator drives the steps by hand.
Migration ordering
Migrations land before the new app container takes traffic. The canonical command sequence on a single-instance host:
# 1. Pull the new image.
docker pull ghcr.io/mbombeck/healthlog:vX.Y.Z
# 2. Run migrations against the live DB using the new image.
docker run --rm --env-file .env.production \
ghcr.io/mbombeck/healthlog:vX.Y.Z \
pnpm prisma migrate deploy
# 3. Recreate the app container so it picks up the new image.
docker compose up -d app
The entrypoint also runs prisma migrate deploy on container boot, so
step 2 is redundant in normal Coolify flow. It's listed explicitly for
the manual case so an operator can verify the migration result before
flipping traffic.
What goes wrong if you reverse it
If the new app container starts before migrations run, the legacy
schema is missing the columns / indexes / enum values the new code
expects. Symptoms: Prisma client throws P2022 column does not exist
on the first query that touches the new shape, the request path 500s,
and the dispatcher logs fill with the same error until the migration
catches up. Health checks usually keep the container marked "healthy"
because /api/version doesn't exercise the affected tables — so a
naïve load balancer will happily route real traffic into the broken
shell.
Rollback
Rolling back migrations is generally one-way. Prisma's migration
history table tracks applied migrations by hash; the down step is
not auto-generated and most HealthLog migrations are forward-only
(column drops, enum additions, index rebuilds). If a release has to
roll back:
- Roll back the app container to the previous image tag.
- Leave the database schema at the newer migration. The previous app version reads forward-compatible-ish shapes for nearly every release pair shipped on the v1.4.x line.
- If a column-drop migration ships in the rollback target, hand-roll
the column back via a fresh forward migration (
pnpm prisma migrate dev --create-only --name vX_Y_Z_rollback_<col>), edit the SQL, commit, and re-deploy. Nevermigrate resolve --rolled-backagainst production unless you've taken a freshpg_dumpfirst.
Verify the served version
The queued-deploy status is not the source of truth — the version
/api/version actually answers is. A BuildKit layer-cache hit can
re-ship the prior image even when Coolify reports a clean deploy, so
always confirm the served build after a deploy:
# Asserts prod + demo serve 1.9.0, prints each target's buildSha,
# exits non-zero on a mismatch or timeout after a bounded retry loop.
pnpm dlx tsx scripts/assert-deploy.ts 1.9.0
Pass --only=prod / --only=demo to check a single target, or
--attempts=N --interval=ms to widen the retry window while a slow
pull settles.