# ============================================================================= # Flimmer — Produktionsimage # # Drei Stufen, damit im Endbild weder Quellcode noch Build-Werkzeuge landen: # deps — Abhängigkeiten installieren (gut cachebar) # builder — Prisma-Client erzeugen und Next bauen # runner — nur das Nötige: standalone-Server, statische Dateien, Prisma-CLI # ============================================================================= FROM node:24-alpine AS base # Nachbau der glibc-Aufrufe, die Prisma-Engines und Next erwarten RUN apk add --no-cache libc6-compat ENV NEXT_TELEMETRY_DISABLED=1 # --- Abhängigkeiten ---------------------------------------------------------- FROM base AS deps WORKDIR /app COPY package.json package-lock.json ./ # npm blockt Install-Skripte seit Version 11 standardmäßig. Prisma braucht seine # eigenen, um die passende Engine für die Plattform bereitzulegen — deshalb hier # gezielt erlaubt statt pauschal. RUN npm ci --no-audit --no-fund --allow-scripts "prisma,@prisma/engines" # --- Build ------------------------------------------------------------------- FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # Beim Bauen wird keine echte Datenbank angefasst; die Platzhalter verhindern # nur, dass die Konfigurationsprüfung mangels Werten abbricht. ENV DATABASE_URL="postgresql://build:build@localhost:5432/build?schema=public" ENV AUTH_SECRET="build-time-placeholder-nicht-verwenden" ENV NODE_ENV=production RUN npx prisma generate && npx next build # --- Prisma-CLI -------------------------------------------------------------- # Eigene Stufe, damit die CLI samt ihrer transitiven Abhängigkeiten vollständig # ins Laufzeitimage kommt. Einzelne Pakete aus node_modules herauszupicken ging # schief: @prisma/config braucht weitere Module, die dabei fehlten. FROM base AS prisma-cli WORKDIR /cli RUN npm init -y > /dev/null && npm install --omit=dev --no-audit --no-fund --allow-scripts "prisma,@prisma/engines" prisma@7.10.0 # --- Laufzeit ---------------------------------------------------------------- FROM base AS runner WORKDIR /app ENV NODE_ENV=production ENV PORT=3000 ENV HOSTNAME=0.0.0.0 ENV DATA_DIR=/app/data RUN addgroup -g 1001 -S nodejs \ && adduser -S -u 1001 -G nodejs flimmer COPY --from=builder /app/public ./public COPY --from=builder --chown=flimmer:nodejs /app/.next/standalone ./ COPY --from=builder --chown=flimmer:nodejs /app/.next/static ./.next/static # Schema und Migrationen bleiben im Image, damit der Container beim Start seine # eigenen Migrationen ausspielen kann (`prisma migrate deploy`). COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/prisma.config.mjs ./prisma.config.mjs # Die CLI liegt bewusst unter /node_modules statt in /app/node_modules: dort # stört sie den getracten Abhängigkeitsbaum der Anwendung nicht, und Node # findet sie beim Auflösen von "prisma/config" trotzdem — die Suche wandert # von /app aus nach oben. COPY --from=prisma-cli /cli/node_modules /node_modules COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh \ && mkdir -p /app/data/images \ && chown -R flimmer:nodejs /app/data USER flimmer EXPOSE 3000 VOLUME ["/app/data"] HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \ CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||3000)+'/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] CMD ["node", "server.js"]