# Multi-stage build for optimized production image FROM node:20 AS base # Install dependencies only when needed FROM base AS deps RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/* WORKDIR /app # Copy package files first for better caching COPY package.json package-lock.json* ./ RUN npm ci --only=production && npm cache clean --force # Rebuild the source code only when needed FROM base AS builder WORKDIR /app # Copy package files first for better caching COPY package.json package-lock.json* ./ # Install all dependencies (including dev dependencies for build) # Use npm ci with cache mount for faster builds RUN --mount=type=cache,target=/root/.npm \ npm ci # Copy Prisma schema first (for better caching) COPY prisma ./prisma # Generate Prisma client (cached if schema unchanged) RUN npx prisma generate # Copy source code (this invalidates cache when code changes) COPY . . # Build the application ENV NEXT_TELEMETRY_DISABLED=1 ENV NODE_ENV=production RUN npm run build # Verify standalone output was created and show structure for debugging RUN if [ ! -d .next/standalone ]; then \ echo "ERROR: .next/standalone directory not found!"; \ echo "Contents of .next directory:"; \ ls -la .next/ || true; \ echo "Checking if standalone exists in different location:"; \ find .next -name "standalone" -type d || true; \ exit 1; \ fi && \ echo "✅ Standalone output found" && \ ls -la .next/standalone/ && \ echo "Standalone structure:" && \ find .next/standalone -type f -name "server.js" || echo "server.js not found in standalone" # Production image, copy all the files and run next FROM base AS runner WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 # Install curl for health checks RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/* # Create a non-root user RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs # Copy the built application 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 standalone output (contains server.js and all dependencies) # The standalone output structure is: .next/standalone/ (not .next/standalone/app/) # Next.js creates: .next/standalone/server.js, .next/standalone/.next/, .next/standalone/node_modules/ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static # Copy Prisma files COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma COPY --from=builder /app/node_modules/prisma ./node_modules/prisma COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma # Create scripts directory and copy start script AFTER standalone to ensure it's not overwritten RUN mkdir -p scripts && chown nextjs:nodejs scripts COPY --from=builder --chown=nextjs:nodejs /app/scripts/start-with-migrate.js ./scripts/start-with-migrate.js # Note: Environment variables should be passed via docker-compose or runtime environment # DO NOT copy .env files into the image for security reasons USER nextjs EXPOSE 3000 ENV PORT=3000 ENV HOSTNAME="0.0.0.0" # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:3000/api/health || exit 1 CMD ["node", "scripts/start-with-migrate.js"]