# syntax=docker/dockerfile:1 # FlorianNetz — multi-stage Build des Next.js-Standalone-Servers. # Stufen: deps (Abhängigkeiten) -> builder (Build) -> runner (schlankes Laufzeit-Image). # Läuft non-root (UID/GID 1001). Migration + optionaler Seed laufen im Entrypoint # vor dem App-Start (siehe docker/entrypoint.sh). ARG NODE_VERSION=22 # --- deps: Produktions- und Build-Abhängigkeiten installieren ----------------- FROM node:${NODE_VERSION}-alpine AS deps WORKDIR /app # Nur Manifeste kopieren -> Layer-Cache bleibt stabil, solange sich Deps nicht ändern. COPY package.json package-lock.json ./ RUN npm ci # --- builder: Next.js im Standalone-Modus bauen ------------------------------- FROM node:${NODE_VERSION}-alpine AS builder WORKDIR /app ENV NEXT_TELEMETRY_DISABLED=1 COPY --from=deps /app/node_modules ./node_modules COPY . . # next.config.ts setzt output:"standalone" -> erzeugt .next/standalone/server.js. RUN npm run build # Katalog-Seed zu einer selbstständigen Plain-ESM-Datei (docker/seed.mjs) # bündeln, damit RUN_SEED=true im Runner (ohne tsx/src) funktioniert. RUN node scripts/build-seed-bundle.mjs # --- runner: minimales Laufzeit-Image ---------------------------------------- FROM node:${NODE_VERSION}-alpine AS runner WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 ENV PORT=3000 ENV HOSTNAME=0.0.0.0 # Postgres-Client (pg_isready/psql) für die Wait-on-DB-Probe im Entrypoint. RUN apk add --no-cache postgresql-client # Non-root-Benutzer (feste UID/GID 1001, wie im Plan gefordert). RUN addgroup --system --gid 1001 nodejs \ && adduser --system --uid 1001 nextjs # Standalone-Server + statische Assets. COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static COPY --from=builder --chown=nextjs:nodejs /app/public ./public # Migration zur Laufzeit: Drizzle-Journal + Migrator + pg, plus die Migrationen. COPY --from=builder --chown=nextjs:nodejs /app/drizzle ./drizzle COPY --from=builder --chown=nextjs:nodejs /app/node_modules/drizzle-orm ./node_modules/drizzle-orm COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg ./node_modules/pg COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg-pool ./node_modules/pg-pool COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg-protocol ./node_modules/pg-protocol COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg-types ./node_modules/pg-types COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg-connection-string ./node_modules/pg-connection-string COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pgpass ./node_modules/pgpass COPY --from=builder --chown=nextjs:nodejs /app/node_modules/postgres-array ./node_modules/postgres-array COPY --from=builder --chown=nextjs:nodejs /app/node_modules/postgres-bytea ./node_modules/postgres-bytea COPY --from=builder --chown=nextjs:nodejs /app/node_modules/postgres-date ./node_modules/postgres-date COPY --from=builder --chown=nextjs:nodejs /app/node_modules/postgres-interval ./node_modules/postgres-interval COPY --from=builder --chown=nextjs:nodejs /app/node_modules/split2 ./node_modules/split2 # Tiefere Transitiv-Abhängigkeiten der pg-Kette mit eagerem require (sonst # ERR_MODULE_NOT_FOUND beim Container-Start in docker/migrate.mjs/seed.mjs): # pg-types/lib/binaryParsers.js -> require('pg-int8') # postgres-interval/index.js -> require('xtend/mutable') COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg-int8 ./node_modules/pg-int8 COPY --from=builder --chown=nextjs:nodejs /app/node_modules/xtend ./node_modules/xtend # Migrations-Runner (plain ESM, ohne tsx) + gebündelter Seed + Entrypoint. COPY --chown=nextjs:nodejs docker/migrate.mjs ./docker/migrate.mjs COPY --from=builder --chown=nextjs:nodejs /app/docker/seed.mjs ./docker/seed.mjs COPY --chown=nextjs:nodejs docker/entrypoint.sh ./docker/entrypoint.sh RUN chmod +x ./docker/entrypoint.sh USER nextjs EXPOSE 3000 # Liveness-Probe (öffentlich, ohne Fachdaten). HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=5 \ CMD wget -q -O - http://127.0.0.1:3000/api/health || exit 1 ENTRYPOINT ["./docker/entrypoint.sh"] CMD ["node", "server.js"]