# syntax=docker/dockerfile:1 # Multi-stage Dockerfile for Next.js 15 (App Router, output: 'standalone') # Based on the official Next.js Docker example, tuned for Coolify. # Reference: https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile # ---------------------------------------------------------------------------- # Base image # ---------------------------------------------------------------------------- FROM node:22-alpine AS base # libc6-compat is recommended by the Next.js image for some native deps (sharp, etc.) RUN apk add --no-cache libc6-compat WORKDIR /app # ---------------------------------------------------------------------------- # 1. Dependencies — installed in a dedicated layer so they are cached as long # as package.json / lockfile / .npmrc do not change. # ---------------------------------------------------------------------------- FROM base AS deps COPY package.json package-lock.json .npmrc ./ # Reproducible install honouring the lockfile (.npmrc provides legacy-peer-deps). RUN npm ci # ---------------------------------------------------------------------------- # 2. Builder — produces the standalone output. # NEXT_PUBLIC_* values are inlined into the client bundle at BUILD time, # so they must be passed as build args. DEV_MODE is read in app/layout.js # to enable Google Analytics, so it must also be present at build time. # ---------------------------------------------------------------------------- FROM base AS builder WORKDIR /app # Build-time arguments (Coolify: set these as Build Variables) ARG NEXT_PUBLIC_API_URL ARG NEXT_PUBLIC_CLIENT_ID ARG NEXT_PUBLIC_CLIENT_SECRET ARG DEV_MODE=FALSE # Expose them to `next build` ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL ENV NEXT_PUBLIC_CLIENT_ID=$NEXT_PUBLIC_CLIENT_ID ENV NEXT_PUBLIC_CLIENT_SECRET=$NEXT_PUBLIC_CLIENT_SECRET ENV DEV_MODE=$DEV_MODE ENV NEXT_TELEMETRY_DISABLED=1 ENV NODE_ENV=production COPY --from=deps /app/node_modules ./node_modules COPY . . RUN npm run build # ---------------------------------------------------------------------------- # 3. Runner — minimal production image running the standalone server. # ---------------------------------------------------------------------------- FROM base AS runner WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 # The standalone server must bind to all interfaces inside the container. ENV HOSTNAME=0.0.0.0 ENV PORT=3000 # Run as a non-root user for security. RUN addgroup --system --gid 1001 nodejs \ && adduser --system --uid 1001 nextjs # Public assets (served by the standalone server). COPY --from=builder /app/public ./public # Standalone output already contains a minimal node_modules + server.js. COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ # Static assets are NOT included in standalone and must be copied separately. COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 # server.js is generated by Next.js at the root of the standalone output. CMD ["node", "server.js"]