Makes schema changes deploy automatically: edit schema -> db:generate -> commit -> push -> Dokploy redeploys -> migrations apply on container start. No more Dokploy database terminal. Components: - src/db/migrate.ts: standalone migrator (single short-lived connection, fails loud on error so a bad migration crashes the container instead of letting the app serve a half-migrated schema) - scripts/build-migrator.mjs: esbuild bundles migrate.ts -> dist/migrate.mjs with drizzle-orm + postgres inlined. Needed because Next.js standalone output keeps neither as a separate node_modules package. - Dockerfile: builder runs db:build-migrator; runner copies migrate.mjs + drizzle/; CMD is 'node migrate.mjs && node server.js' - package.json: db:generate / db:migrate / db:studio / db:pull / db:build-migrator scripts; esbuild promoted to an explicit devDependency - pnpm-lock.yaml resynced BUG FIX: .dockerignore had 'drizzle/' — migration SQL was excluded from the build context, so even a correct Dockerfile COPY would have found nothing. This was the second half (with the .gitignore bug in commit 1) of why the migration pipeline never worked. Now only _archived/_introspected are excluded. Verified: full docker build succeeds; runner image contains migrate.mjs + drizzle baseline; migrator tested end-to-end against a scratch DB (35 tables created, __drizzle_migrations populated, idempotent on rerun).
42 lines
1.6 KiB
Docker
42 lines
1.6 KiB
Docker
# Stage 1: Install dependencies
|
|
FROM node:22-alpine AS deps
|
|
RUN apk add --no-cache libc6-compat
|
|
WORKDIR /app
|
|
COPY package.json pnpm-lock.yaml ./
|
|
RUN corepack enable pnpm
|
|
RUN pnpm install --ignore-scripts
|
|
|
|
# Stage 2: Build the app
|
|
FROM node:22-alpine AS builder
|
|
RUN apk add --no-cache libc6-compat openssl
|
|
WORKDIR /app
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
COPY . .
|
|
RUN corepack enable pnpm
|
|
RUN pnpm run build
|
|
# Bundle the standalone migration runner. This produces dist/migrate.mjs
|
|
# with drizzle-orm + postgres inlined, so the runner stage needs no extra
|
|
# node_modules. Runs here because the builder has full dependencies.
|
|
RUN pnpm run db:build-migrator
|
|
|
|
# Stage 3: Production runner
|
|
FROM node:22-alpine AS runner
|
|
WORKDIR /app
|
|
ENV NODE_ENV=production
|
|
RUN addgroup --system --gid 1001 nodejs
|
|
RUN adduser --system --uid 1001 nextjs
|
|
COPY --from=builder /app/public ./public
|
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
COPY --from=builder /app/.next/static .next/static
|
|
# Migration runner + migration SQL. The migrator runs on container start
|
|
# (see CMD) BEFORE the Next.js server boots — see drizzle/README.md.
|
|
COPY --from=builder --chown=nextjs:nodejs /app/dist/migrate.mjs ./migrate.mjs
|
|
COPY --from=builder --chown=nextjs:nodejs /app/drizzle ./drizzle
|
|
USER nextjs
|
|
EXPOSE 3000
|
|
ENV PORT=3000
|
|
ENV HOSTNAME="0.0.0.0"
|
|
# Apply pending migrations, THEN start the server. If migration fails the
|
|
# process exits non-zero and the container crashes — a loud, safe failure
|
|
# that prevents the app from serving against a half-migrated schema.
|
|
CMD ["sh", "-c", "node migrate.mjs && node server.js"]
|