# Multi-stage Dockerfile for Next.js Blog Application # Optimized for Static Site Generation with standalone output # Final image size: ~150MB # ============================================ # Stage 1: Dependencies Installation # ============================================ FROM node:22-alpine AS deps # Install libc6-compat for better compatibility RUN apk add --no-cache libc6-compat WORKDIR /app # Copy package files for dependency installation # These files are copied first to leverage Docker layer caching # If package.json hasn't changed, this layer will be reused COPY package.json package-lock.json* ./ # Install dependencies using npm ci for reproducible builds # --only=production flag is not used here because we need dev dependencies for build RUN npm ci # ============================================ # Stage 2: Build Next.js Application # ============================================ FROM node:22-alpine AS builder WORKDIR /app # Copy dependencies from deps stage COPY --from=deps /app/node_modules ./node_modules # Copy .env file for build-time variables # This file is created by CI/CD workflow from Gitea secrets # NEXT_PUBLIC_* variables are embedded in client-side bundle during build COPY .env* ./ # Copy all application source code # This includes: # - app/ directory (Next.js 16 App Router) # - components/ directory # - lib/ directory (markdown utilities) # - content/blog/ directory (markdown blog posts) # - public/ directory (static assets) # - next.config.js, tsconfig.json, tailwind.config.js, etc. COPY . . # Set environment variables for build ENV NEXT_TELEMETRY_DISABLED=1 ENV NODE_ENV=production # Build the Next.js application # This will: # 1. Process all markdown files from content/blog/ # 2. Generate static pages for all blog posts (SSG) # 3. Create standalone output in .next/standalone/ # 4. Optimize images and assets # 5. Bundle and minify JavaScript RUN npm run build # ============================================ # Stage 3: Production Runtime # ============================================ FROM node:22-alpine AS runner # Install curl for health checks RUN apk add --no-cache curl WORKDIR /app # Set production environment ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 # Create a non-root user for security # The application will run as this user instead of root RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs # Copy only the necessary files from builder stage # Next.js standalone output includes all dependencies needed to run COPY --from=builder /app/public ./public # Copy standalone output (includes minimal node_modules and server files) COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ # Copy static files (CSS, JS bundles, optimized images) COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static # Create directories for logs (optional, if your app writes logs) RUN mkdir -p /app/logs && chown nextjs:nodejs /app/logs # Switch to non-root user USER nextjs # Expose the application port # Note: This matches the port in package.json "dev" script (-p 3030) EXPOSE 3030 # Set the port environment variable ENV PORT=3030 ENV HOSTNAME="0.0.0.0" # Health check to verify the application is running # Docker will periodically check this endpoint # If it fails, the container is marked as unhealthy HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ CMD curl -f http://localhost:3030/ || exit 1 # Start the Next.js server # The standalone output includes a minimal server.js file CMD ["node", "server.js"]