client.end() in the finally block was throwing (postgres driver already
cleaned up connections internally during migrate()), causing an unhandled
rejection and exit code 1 despite the migration succeeding.
- Use explicit process.exit(0)/exit(1) instead of relying on implicit exit
- Wrap both client.end() calls with .catch(() => {}) to tolerate cleanup errors
- Add onnotice: () => {} to suppress noisy NOTICE messages (schema/table
already exists) that are normal on every idempotent re-run
https://claude.ai/code/session_01ReaqH3o9NVH4QU4qE9WMMQ
* Use Docker Compose init service pattern for database migrations
Replaces the fragile double-migration approach (server.ts startup +
drizzle-kit CLI in CI) with a one-shot migrate service in Docker Compose
that the app depends on via condition: service_completed_successfully.
- Add scripts/migrate.mjs: programmatic drizzle-orm migration (uses
production deps, not drizzle-kit which is devOnly and absent from image)
- Dockerfile: copy scripts/ into image, remove drizzle.config.ts (only
needed by drizzle-kit CLI)
- server.ts: remove runMigrations() entirely; migrations are now handled
by the migrate container before the app starts
- deploy.yml: remove explicit docker run migration step; replace sleep 10
health check with a poll loop and explicit migrate exit-code check
Production server's docker-compose.yaml needs a one-time manual update
to add the migrate service — see plan for exact config.
https://claude.ai/code/session_01ReaqH3o9NVH4QU4qE9WMMQ
* Move drizzle-kit to devDependencies; use docker compose wait in deploy
drizzle-kit is a dev-only tool (schema generation and local migrations).
Now that production migrations run via the programmatic drizzle-orm API
in the migrate init container, drizzle-kit has no runtime role.
Also replaces the polling health check loop with docker compose wait,
which blocks until the migrate service exits and returns its exit code
cleanly — no sleep or manual status inspection needed.
https://claude.ai/code/session_01ReaqH3o9NVH4QU4qE9WMMQ
---------
Co-authored-by: Claude <noreply@anthropic.com>