# Multi-stage Dockerfile for Next.js with Prisma FROM node:20-alpine AS base # Install pnpm ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable # Install dependencies only when needed FROM base AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. RUN apk add --no-cache libc6-compat WORKDIR /app # Install dependencies based on the preferred package manager COPY package.json pnpm-lock.yaml* ./ # Copy Prisma schema for postinstall script COPY prisma ./prisma # Set dummy DATABASE_URL for Prisma client generation during postinstall ENV DATABASE_URL="file:/tmp/build.db" RUN \ if [ -f pnpm-lock.yaml ]; then pnpm install --frozen-lockfile; \ else echo "Lockfile not found." && exit 1; \ fi # Rebuild the source code only when needed FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # Set a dummy DATABASE_URL for build time (Prisma needs it to generate client) ENV DATABASE_URL="file:/tmp/build.db" # Generate Prisma client (no DB needed at build time) RUN pnpm prisma generate # Build the application RUN pnpm run build # Production image, copy all the files and run next FROM base AS runner # Set timezone to Europe/Paris and install sqlite3 for backups RUN apk add --no-cache tzdata sqlite su-exec RUN ln -snf /usr/share/zoneinfo/Europe/Paris /etc/localtime && echo Europe/Paris > /etc/timezone WORKDIR /app ENV NODE_ENV=production # Uncomment the following line in case you want to disable telemetry during runtime. # ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs # Copy the public folder COPY --from=builder /app/public ./public # Set the correct permission for prerender cache RUN mkdir .next RUN chown nextjs:nodejs .next # Automatically leverage output traces to reduce image size # https://nextjs.org/docs/advanced-features/output-file-tracing COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static # Copy Prisma schema and migrations COPY --from=builder /app/prisma ./prisma # Copy pnpm node_modules (includes .pnpm store with Prisma client) COPY --from=builder /app/node_modules ./node_modules # Create data directory for SQLite and backups (will be overridden by volume mount but ensures it exists) RUN mkdir -p /app/data/backups && chmod -R 777 /app/data # Set all ENV vars before switching user ENV PORT=3000 ENV HOSTNAME="0.0.0.0" ENV TZ=Europe/Paris EXPOSE 3000 # Start the application with Prisma migrations # Fix permissions for data directory (volume mount may have wrong ownership) # Then switch to nextjs user and run migrations # For fresh DBs: use db push to apply schema, then mark migrations as applied # For existing DBs: use migrate deploy to apply incremental migrations CMD ["sh", "-c", "mkdir -p /app/data/backups && chown -R nextjs:nodejs /app || chmod -R 755 /app || true; chmod -R 777 /app/data && chown -R nextjs:nodejs /app/data || true; exec su-exec nextjs sh -c 'set +e; if ! pnpm prisma migrate deploy; then echo \"Migration failed, using db push for fresh database...\"; pnpm prisma db push --accept-data-loss --skip-generate; for migration in prisma/migrations/*/; do if [ -d \"$migration\" ] && [ -f \"$migration/migration.sql\" ]; then migration_name=$(basename \"$migration\"); pnpm prisma migrate resolve --applied \"$migration_name\" 2>/dev/null || true; fi; done; fi; set -e; exec node server.js'"]