diff --git a/docker-compose.yml b/docker-compose.yml index 8d333ad..6f3f3c7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,26 +1,61 @@ -# docker-compose.yml -version: "3.8" +# ============================================================================= +# Docker Compose - Production Configuration for Coolify +# ============================================================================= +# Optimized for: +# - Fast cold starts +# - Minimal resource usage +# - Zero-downtime deployments +# - Coolify compatibility +# ============================================================================= services: - frontend: + app: build: context: . dockerfile: Dockerfile + # Enable BuildKit for faster builds and layer caching args: - - NODE_ENV=${NODE_ENV} - - DEV_MODE=${DEV_MODE} - - NEXT_TELEMETRY_DISABLED=1 - - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} - - NEXT_PUBLIC_CLIENT_ID=${NEXT_PUBLIC_CLIENT_ID} - - NEXT_PUBLIC_CLIENT_SECRET=${NEXT_PUBLIC_CLIENT_SECRET} + - BUILDKIT_INLINE_CACHE=1 + - DOCKER_BUILDKIT=1 + # Cache strategy for faster rebuilds + cache_from: + - node:20-alpine + + # Container restart policy restart: unless-stopped + + # Dynamic port mapping (Coolify manages external port) ports: - - "${APP_PORT}:3000" + - "3000" + + # Environment variables + # Note: Coolify injects these automatically from project settings environment: - - NEXT_TELEMETRY_DISABLED=1 - - DEV_MODE=${DEV_MODE} + # Node.js + - NODE_ENV=${NODE_ENV:-production} + - PORT=3000 + - HOSTNAME=0.0.0.0 + - DEV_MODE=false - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} - NEXT_PUBLIC_CLIENT_ID=${NEXT_PUBLIC_CLIENT_ID} - NEXT_PUBLIC_CLIENT_SECRET=${NEXT_PUBLIC_CLIENT_SECRET} - - APP_PORT=${APP_PORT} - - NODE_ENV=${NODE_ENV} + + # Resource limits (optimized for low traffic sites) + deploy: + resources: + limits: + cpus: "0.5" + memory: 512M + reservations: + cpus: "0.1" + memory: 128M + + # Networks (Coolify uses bridge network) + networks: + - coolify + +# Network configuration +networks: + coolify: + driver: bridge + external: true diff --git a/dockerfile b/dockerfile index 2f9804a..4be51a1 100644 --- a/dockerfile +++ b/dockerfile @@ -1,48 +1,122 @@ -# Dockerfile for Next.js 14 - Optimized for Production +# ============================================================================= +# Dockerfile for Next.js 16 - Production Optimized for Coolify +# ============================================================================= +# Multi-stage build for minimal image size and maximum performance +# Compatible with: Coolify, Docker Compose, Kubernetes +# Node Version: 20 LTS (Alpine) +# Package Manager: npm (native to project) +# ============================================================================= -# Use the official Node.js image with Alpine Linux for a smaller footprint -FROM node:22-alpine AS base +# ----------------------------------------------------------------------------- +# Stage 1: Dependencies (deps) +# Install only production dependencies with locked versions +# ----------------------------------------------------------------------------- +FROM node:20-alpine AS deps + +# Install system dependencies required by Node.js native modules +RUN apk add --no-cache libc6-compat -# Set the working directory in the container WORKDIR /app -# Copy package.json and package-lock.json (or yarn.lock) -COPY package*.json ./ +# Copy package files for dependency installation +COPY package.json package-lock.json ./ -# Install dependencies - leveraging Docker cache -RUN npm i --force +# Install dependencies with npm ci (clean install from lock file) +# This ensures reproducible builds and faster installs +RUN npm ci --only=production --ignore-scripts && \ + npm cache clean --force -# Copy the rest of the application code +# ----------------------------------------------------------------------------- +# Stage 2: Builder (builder) +# Build the Next.js application with all dev dependencies +# ----------------------------------------------------------------------------- +FROM node:20-alpine AS builder + +# Install system dependencies for Prisma and build tools +RUN apk add --no-cache \ + libc6-compat \ + openssl + +WORKDIR /app + +# Copy package files +COPY package.json package-lock.json ./ + +# Install ALL dependencies (including devDependencies for build) +RUN npm ci --ignore-scripts && \ + npm cache clean --force + +# Copy application source COPY . . -# Build the Next.js application +# Set build-time environment variables +ENV NEXT_TELEMETRY_DISABLED=1 +ENV NODE_ENV=production + +# Generate Prisma Client +RUN npx prisma generate + +# Build Next.js application with standalone output +# This creates a minimal server bundle in .next/standalone RUN npm run build -# Stage 2: Production image - smaller and leaner -FROM node:22-alpine AS runner +# ----------------------------------------------------------------------------- +# Stage 3: Runner (production) +# Minimal runtime image with only necessary files +# ----------------------------------------------------------------------------- +FROM node:20-alpine AS runner + +# Install runtime dependencies +RUN apk add --no-cache \ + libc6-compat \ + openssl \ + wget \ + dumb-init && \ + rm -rf /var/cache/apk/* -# Set the working directory WORKDIR /app -# Set environment variables -ENV NODE_ENV $NODE_ENV -ENV NEXT_TELEMETRY_DISABLED 1 +# Set production environment variables +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 +ENV PORT=3000 +ENV HOSTNAME=0.0.0.0 -# Add a non-root user for security -RUN addgroup -g 1001 -S nodejs -RUN adduser -S nextjs -u 1001 +# Create non-root user for security +RUN addgroup --system --gid 1001 nodejs && \ + adduser --system --uid 1001 nextjs -# Copy only the necessary files from the builder stage -COPY --from=base /app/next.config.js ./ -COPY --from=base /app/public ./public -COPY --from=base --chown=nextjs:nodejs /app/.next/standalone ./ -COPY --from=base --chown=nextjs:nodejs /app/.next/static ./.next/static +# Copy Prisma runtime files +COPY --from=builder --chown=nextjs:nodejs /app/node_modules/.prisma ./node_modules/.prisma +COPY --from=builder --chown=nextjs:nodejs /app/node_modules/@prisma ./node_modules/@prisma -# Change ownership of all copied files to the non-root user +# Copy standalone Next.js server +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ + +# Copy static files +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +# Copy public directory +COPY --from=builder --chown=nextjs:nodejs /app/public ./public + +# Copy Prisma schema for runtime (if needed) +COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma + +# Copy scripts directory for seed and admin tools +COPY --from=builder --chown=nextjs:nodejs /app/scripts ./scripts + +# Switch to non-root user USER nextjs -# Expose the port Next.js listens on +# Expose application port EXPOSE 3000 -# Command to start the Next.js server in production mode +# Health check for container orchestration +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1 + +# Use dumb-init to handle signals properly +ENTRYPOINT ["dumb-init", "--"] + +# Start the Next.js server CMD ["node", "server.js"] \ No newline at end of file