Skip to content

Generated projects cannot run migrations in containers — promote the compiled-migrator pattern from the blog example #123

Description

@PAMulligan

Description

The generated multi-stage Dockerfile ends its build stage with pnpm prune --prod, so both images a generated project can produce lack dev tooling: target: build has drizzle-kit/tsx pruned away, and the production stage never had them. There is consequently no way to run database migrations inside the containers a generated project ships — pnpm db:migrate (drizzle-kit) only works on a developer machine with dev dependencies installed.

Discovered while wiring the blog example's compose stack (#121): a migrate one-shot service pointed at target: build failed with Command "drizzle-kit" not found (exit 254), and tsx: not found for the seed.

Why

The generated docker-compose.yml orchestrates API + PostgreSQL, but a fresh docker compose up serves the API against an empty, unmigrated database (the health check reports degraded). The Railway and Fly.io READMEs work around the same gap by running migrations from the host (railway run pnpm db:migrate, fly mpg proxy); the node/docker path has no in-container story at all.

examples/blog-api-node ships the solution: a compiled programmatic runner (src/db/migrate.ts, using drizzle-orm/postgres-js/migrator — a runtime dependency) executed as node dist/db/migrate.js by a one-shot compose service inside the same production image as the API. One image, no dev tooling in containers, API start gated on service_completed_successfully. The same pattern would also unlock Fly.io's release_command (currently ruled out in the generated Fly README precisely because the production image prunes drizzle-kit).

Acceptance Criteria

  • Add a src/db/migrate.ts snippet (e.g. templates/snippets/shared/src/db/) and have setup-project.sh copy it for the node, railway, and fly targets; it must compile into dist/ with the regular pnpm build
  • templates/node-server/docker-compose.yml gains the one-shot migrate service (same production image via a shared image: tag); api depends on migrate: service_completed_successfully
  • Production image continues to copy src/db/migrations (journal + SQL available at runtime — already the case)
  • Snippets type-check in the typecheck-templates CI job and freshly generated projects still pass the generated-projects job
  • Railway/Fly generated READMEs updated to mention the in-image migrator (e.g. Fly release_command = "node dist/db/migrate.js") as an alternative to host-side migration
  • deployment-config-generator skill and docs updated where they describe the compose/migration story
  • CI passes

Resources

  • examples/blog-api-node/api/src/db/migrate.ts — working implementation
  • examples/blog-api-node/api/docker-compose.yml — migrate service wiring (image sharing, dependency gating, in-container seed command)
  • PR feat: add blog API Node.js/Docker example #121 — "Migrations in containers" design note
  • templates/node-server/Dockerfile — build-stage pnpm prune --prod (why target: build has no dev deps)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions