From 4e23350bfc2afd119bff68421e70c771bf53971e Mon Sep 17 00:00:00 2001 From: Vadym Samoilenko Date: Mon, 23 Feb 2026 14:14:18 +0000 Subject: [PATCH] fix: run migrations via Next.js instrumentation.ts at startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tsx v4 + Node.js 22 ESM does not handle extensionless .ts imports. instrumentation.ts uses compiled/bundled code — no resolution issues. Migrations run automatically before first request on every app start. Removes external migrator container approach entirely. Co-Authored-By: Claude Sonnet 4.6 --- deploy.sh | 18 +++++++++++++++--- docker-compose.prod.yml | 13 ------------- src/instrumentation.ts | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 16 deletions(-) create mode 100644 src/instrumentation.ts diff --git a/deploy.sh b/deploy.sh index b3db676..8719078 100755 --- a/deploy.sh +++ b/deploy.sh @@ -255,9 +255,21 @@ start_containers() { run_migrations() { header "Phase 5 — Database migrations" - log "Running Payload CMS migrations (via build stage — has pnpm + full node_modules)..." - docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" --profile migrate run --rm migrator - log "Migrations complete." + # Migrations run automatically via src/instrumentation.ts on Next.js startup. + # We wait for the app container to be healthy (confirming startup completed). + log "Waiting for app container to pass health check (migrations run at startup)..." + local max_wait=120 + local elapsed=0 + while ! docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T app \ + node -e "process.exit(0)" >/dev/null 2>&1; do + if [ "$elapsed" -ge "$max_wait" ]; then + warn "App health check timed out — check logs: docker compose -f $COMPOSE_FILE logs app" + return + fi + sleep 3 + elapsed=$((elapsed + 3)) + done + log "App is running. Migrations were applied at startup via instrumentation.ts" } # ============================================================================= diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 52fee35..fc0e82f 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -12,19 +12,6 @@ services: db: condition: service_healthy - migrator: - build: - context: . - target: migrator - command: pnpm payload migrate - env_file: - - .env.production - depends_on: - db: - condition: service_healthy - profiles: - - migrate - db: image: postgres:17-alpine restart: always diff --git a/src/instrumentation.ts b/src/instrumentation.ts new file mode 100644 index 0000000..86ca29b --- /dev/null +++ b/src/instrumentation.ts @@ -0,0 +1,14 @@ +// Runs on Next.js startup (Node.js runtime only) before any requests are handled. +// Uses compiled/bundled code — no tsx/ESM resolution issues. +export async function register() { + if (process.env.NEXT_RUNTIME === 'nodejs') { + const { getPayload } = await import('payload'); + const { default: config } = await import('@payload-config'); + + const payload = await getPayload({ config }); + + console.log('[startup] Running database migrations...'); + await payload.db.migrate(); + console.log('[startup] Migrations complete.'); + } +}