From 93cdce490568ea01fda77264bb19d1c58090b595 Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 19 May 2025 00:00:19 +0200 Subject: [PATCH] chore: optimize Docker build with multi-stage layers and add performance-tuned Next.js config --- Dockerfile | 58 ++++++++++++++++++++++++--------------- myfavstuff/next.config.js | 44 +++++++++++++++++++++++++---- 2 files changed, 75 insertions(+), 27 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9494b69..2239faf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,21 @@ -# Production-ready Dockerfile for Next.js +# Optimized Dockerfile for Next.js on Coolify # Receive build arguments passed by Coolify for the builder stage ARG NEXT_PUBLIC_SUPABASE_URL ARG NEXT_PUBLIC_SUPABASE_ANON_KEY # Builder stage +FROM node:20-alpine AS deps +WORKDIR /app + +# Install dependencies only when needed (better layer caching) +COPY myfavstuff/package.json myfavstuff/package-lock.json* ./ + +# Install dependencies using frozen lockfile for consistent builds +# Use clean-install for production dependencies only +RUN npm ci --only=production + +# Rebuild the source code only when needed FROM node:20-alpine AS builder WORKDIR /app @@ -14,49 +25,52 @@ ARG NEXT_PUBLIC_SUPABASE_ANON_KEY ENV NODE_ENV=production -# Copy package.json and package-lock.json from the 'myfavstuff' subdirectory -COPY myfavstuff/package.json myfavstuff/package-lock.json* ./ +# Copy dependencies from deps stage +COPY --from=deps /app/node_modules ./node_modules +COPY myfavstuff/package.json ./package.json -# Install dependencies including dev dependencies needed for build -# This will also install eslint if it's in devDependencies and package-lock.json is up to date -RUN npm ci +# Copy source files +COPY myfavstuff/. . -# Copy the rest of your app's source code from the 'myfavstuff' subdirectory -COPY myfavstuff . +# Set Next.js build memory limit to avoid OOM issues +ENV NODE_OPTIONS="--max_old_space_size=2048" -# Debug the environment variables that npm run build will see -# Pass ARGs directly to the build command's environment -RUN echo "--- Debugging Build Environment Variables (Passed to Build) ---" && \ - echo "NEXT_PUBLIC_SUPABASE_URL (from ARG): $NEXT_PUBLIC_SUPABASE_URL" && \ - echo "NEXT_PUBLIC_SUPABASE_ANON_KEY (from ARG): $NEXT_PUBLIC_SUPABASE_ANON_KEY" && \ - echo "--- End Debugging --- " && \ - NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL \ +# Run the build with environment variables +RUN NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL \ NEXT_PUBLIC_SUPABASE_ANON_KEY=$NEXT_PUBLIC_SUPABASE_ANON_KEY \ npm run build -# Production stage (runner) +# Production stage (runner) - using smaller base image for runtime FROM node:20-alpine AS runner WORKDIR /app +# Use production node environment ENV NODE_ENV=production -# Runtime environment variables will be set by Coolify directly in the container environment. +# Runtime environment variables will be set by Coolify directly in the container environment -# Create a non-root user for security IN THIS STAGE +# Create a non-root user for security RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001 -# Copy standalone output from the builder stage +# Set working directory ownership +RUN chown -R nextjs:nodejs /app + +# Optimize production image size by only copying what's needed COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ -# Copy static assets COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static -# Copy public assets (if any, like favicon, images in /public) COPY --from=builder --chown=nextjs:nodejs /app/public ./public +# Switch to non-root user for better security USER nextjs +# Expose the running port EXPOSE 3000 +# Set server runtime environment ENV PORT=3000 ENV HOSTNAME=0.0.0.0 -# Run the Next.js standalone server +# Optimize for containerized environment +ENV NODE_OPTIONS="--enable-source-maps --max-http-header-size=16384" + +# Use exec form for CMD to properly handle signals CMD ["node", "server.js"] diff --git a/myfavstuff/next.config.js b/myfavstuff/next.config.js index 3a81f8b..5025f75 100644 --- a/myfavstuff/next.config.js +++ b/myfavstuff/next.config.js @@ -2,18 +2,52 @@ const nextConfig = { output: 'standalone', // Enable standalone output for optimized Docker builds - // Configure images if using them from external domains + // Optimize build performance with swcMinify + swcMinify: true, + + // Limit the number of CPU cores used during production build + experimental: { + cpus: Math.max(1, Math.min(4, require('os').cpus().length - 1)), + }, + + // Configure images for Supabase Storage URLs images: { - // domains: ['example.com'], // Add your image domains here if needed + domains: [ + // Extract domain from NEXT_PUBLIC_SUPABASE_URL if available + process.env.NEXT_PUBLIC_SUPABASE_URL ? new URL(process.env.NEXT_PUBLIC_SUPABASE_URL).hostname : '', + ].filter(Boolean), // Remove empty entries + // Optimize image formats for better performance + formats: ['image/webp'], }, serverActions: { - bodySizeLimit: '10mb', // Or your desired limit, e.g., '5mb', '20mb' + bodySizeLimit: '10mb', }, - - // Add any other configurations needed + // Enable better caching for production builds + poweredByHeader: false, reactStrictMode: true, + + // Optimize Webpack + webpack: (config, { dev, isServer }) => { + // Only enable cache during development + if (dev) { + config.cache = true; + } + + // Optimize production build + if (!dev) { + config.optimization = { + ...config.optimization, + // Minimize hashing calculation time + moduleIds: 'deterministic', + // Minimize concurrent processing + sideEffects: true, + }; + } + + return config; + }, }; module.exports = nextConfig;