chore: optimize Docker build with multi-stage layers and add performance-tuned Next.js config

This commit is contained in:
Greg 2025-05-19 00:00:19 +02:00
parent cde9020bb6
commit 93cdce4905
2 changed files with 75 additions and 27 deletions

View File

@ -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 # Receive build arguments passed by Coolify for the builder stage
ARG NEXT_PUBLIC_SUPABASE_URL ARG NEXT_PUBLIC_SUPABASE_URL
ARG NEXT_PUBLIC_SUPABASE_ANON_KEY ARG NEXT_PUBLIC_SUPABASE_ANON_KEY
# Builder stage # 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 FROM node:20-alpine AS builder
WORKDIR /app WORKDIR /app
@ -14,49 +25,52 @@ ARG NEXT_PUBLIC_SUPABASE_ANON_KEY
ENV NODE_ENV=production ENV NODE_ENV=production
# Copy package.json and package-lock.json from the 'myfavstuff' subdirectory # Copy dependencies from deps stage
COPY myfavstuff/package.json myfavstuff/package-lock.json* ./ COPY --from=deps /app/node_modules ./node_modules
COPY myfavstuff/package.json ./package.json
# Install dependencies including dev dependencies needed for build # Copy source files
# This will also install eslint if it's in devDependencies and package-lock.json is up to date COPY myfavstuff/. .
RUN npm ci
# Copy the rest of your app's source code from the 'myfavstuff' subdirectory # Set Next.js build memory limit to avoid OOM issues
COPY myfavstuff . ENV NODE_OPTIONS="--max_old_space_size=2048"
# Debug the environment variables that npm run build will see # Run the build with environment variables
# Pass ARGs directly to the build command's environment RUN NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL \
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 \
NEXT_PUBLIC_SUPABASE_ANON_KEY=$NEXT_PUBLIC_SUPABASE_ANON_KEY \ NEXT_PUBLIC_SUPABASE_ANON_KEY=$NEXT_PUBLIC_SUPABASE_ANON_KEY \
npm run build npm run build
# Production stage (runner) # Production stage (runner) - using smaller base image for runtime
FROM node:20-alpine AS runner FROM node:20-alpine AS runner
WORKDIR /app WORKDIR /app
# Use production node environment
ENV NODE_ENV=production 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 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 --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
# Copy static assets
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static 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 COPY --from=builder --chown=nextjs:nodejs /app/public ./public
# Switch to non-root user for better security
USER nextjs USER nextjs
# Expose the running port
EXPOSE 3000 EXPOSE 3000
# Set server runtime environment
ENV PORT=3000 ENV PORT=3000
ENV HOSTNAME=0.0.0.0 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"] CMD ["node", "server.js"]

View File

@ -2,18 +2,52 @@
const nextConfig = { const nextConfig = {
output: 'standalone', // Enable standalone output for optimized Docker builds 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: { 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: { serverActions: {
bodySizeLimit: '10mb', // Or your desired limit, e.g., '5mb', '20mb' bodySizeLimit: '10mb',
}, },
// Enable better caching for production builds
// Add any other configurations needed poweredByHeader: false,
reactStrictMode: true, 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; module.exports = nextConfig;