Skip to content

Instantly share code, notes, and snippets.

@nurmdrafi
Last active September 4, 2024 06:18
Show Gist options
  • Save nurmdrafi/fd74a91ef82001de1d3a390c1338345a to your computer and use it in GitHub Desktop.
Save nurmdrafi/fd74a91ef82001de1d3a390c1338345a to your computer and use it in GitHub Desktop.
Optimized Dockerfile For Next.js

Using ubuntu base image + NVM (secure)

# Stage 1: Setup Node.js with NVM ===>
FROM ubuntu:24.10 AS base

# Set working directory
WORKDIR /app

# Install dependencies
RUN apt-get update && \
    apt-get install -y curl

# Install NVM
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

# Install Node.js
RUN bash -c "source ~/.nvm/nvm.sh \
    && nvm install 22.0.0 \
    && nvm use 22.0.0 \
    && nvm alias default 22.0.0"

# Make Node.js available globally by copying it to the system-wide path
ENV NODE_VERSION="22.0.0"
ENV NVM_DIR="/root/.nvm"
ENV PATH="$NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH"

# Stage 2: Install dependencies ===>
FROM ubuntu:24.10 AS deps

# Set working directory
WORKDIR /app

# Copy NVM from the base stage
COPY --from=base /root/.nvm /root/.nvm

# Set Node.js environment
ENV NVM_DIR="/root/.nvm"
ENV NODE_VERSION="22.0.0"
ENV PATH="$NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH"

# Copy package.json
COPY package*.json ./

# Install npm packages
RUN npm cache clean --force \
    && npm ci --only=production --force

# Stage 3: Build the project ===>
FROM ubuntu:24.10 AS builder

# Set working directory
WORKDIR /app

# Copy NVM from the base stage
COPY --from=base /root/.nvm /root/.nvm

# Set Node.js environment
ENV NVM_DIR="/root/.nvm"
ENV NODE_VERSION="22.0.0"
ENV PATH="$NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH"

# Copy necessary files
COPY ./ ./
COPY --from=deps /app/node_modules ./node_modules

# Set environment variables
ENV NODE_ENV=production
ENV NEXT_PUBLIC_BASE_URL=NEXT_PUBLIC_BASE_URL
ENV NEXT_PUBLIC_MAP_API_ACCESS_TOKEN=NEXT_PUBLIC_MAP_API_ACCESS_TOKEN
ENV NEXT_PUBLIC_TRACE_BASE_URL=NEXT_PUBLIC_TRACE_BASE_URL
ENV NEXT_PUBLIC_TRACE_API_ACCESS_TOKEN=NEXT_PUBLIC_TRACE_API_ACCESS_TOKEN
ENV SKIP_HUSKY=1
ENV NEXT_TELEMETRY_DISABLED=1

# Build the project
RUN npm run build

# Stage 4: Serve the project ===>
FROM ubuntu:24.10 AS runner

# Set working directory
WORKDIR /app

# Copy NVM and Node.js binaries for non-root user
COPY --from=base /root/.nvm /home/nextjs/.nvm

# Set Node.js environment for non-root user
ENV NODE_VERSION="22.0.0"
ENV NVM_DIR="/home/nextjs/.nvm"
ENV PATH="$NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH"

# Copy necessary files from the builder stage
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/entrypoint.sh ./

# Ensure entrypoint.sh is executable
RUN chmod +x /app/entrypoint.sh

# Create a non-root user and set ownership
RUN groupadd -g 1001 nodejs \
    && useradd -u 1001 -g nodejs -m -s /bin/bash nextjs \
    && chown -R nextjs:nodejs /app

# Ensure Node.js is accessible for the non-root user
RUN bash -c "chown -R nextjs:nodejs /home/nextjs/.nvm"    

# Switch to non-root user
USER nextjs

# Expose port
EXPOSE 3000

# Start the server
ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["node", "server.js"]

Using node base image

# Stage 1: Install Dependencies

# Use an official Node.js runtime as the base image
FROM node:18-alpine AS deps

# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat

# Set the working directory in the container
WORKDIR /app

# Copy package.json and package-lock.json to the container
COPY package.json package-lock.json ./

# Install dependencies using a clean install
RUN npm ci

# Stage 2: Build The Application
FROM node:18-alpine AS builder

WORKDIR /app

# Copy required files from previous layer
COPY --from=deps /app/node_modules ./node_modules

# Copy the rest of the application files to the container
COPY . .

# Set arguments as environment variables
ARG NEXT_PUBLIC_BASE_URL_STAGING
ARG NEXT_PUBLIC_BASE_URL_PRODUCTION
ARG NEXT_PUBLIC_MAP_API_ACCESS_TOKEN

# Set environtment variables
ENV NEXT_PUBLIC_BASE_URL_STAGING=$NEXT_PUBLIC_BASE_URL_STAGING
ENV NEXT_PUBLIC_BASE_URL_PRODUCTION=$NEXT_PUBLIC_BASE_URL_PRODUCTION
ENV NEXT_PUBLIC_MAP_API_ACCESS_TOKEN=$NEXT_PUBLIC_MAP_API_ACCESS_TOKEN

# Build the Next.js application for production
RUN npm run build

# Stage 3: Build Production Image, Copy All Files and Run
FROM node:18-alpine AS runner

WORKDIR /app

# Set appropriate permissions and user for running the application
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Copy assets and the generated standalone server
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

# Disable Next.js telemetry (collects anonymous telemetry data about general usage by default)
# RUN npx next telemetry disable

# CMD npm run start
# Serve the app
CMD ["node", "server.js"]

# Commands
# sudo docker ps
# sudo docker delete <container_id>
# sudo docker rmi -f <image_id>
# sudo docker build -t docker_test -f Dockerfile.test .
# sudo docker run -p 3000:3000 docker_test
# sudo docker build -t docker_test --build-arg NEXT_PUBLIC_BASE_URL_STAGING=NEXT_PUBLIC_BASE_URL_STAGING --build-arg NEXT_PUBLIC_BASE_URL_PRODUCTION=NEXT_PUBLIC_BASE_URL_PRODUCTION --build-arg NEXT_PUBLIC_MAP_API_ACCESS_TOKEN=NEXT_PUBLIC_MAP_API_ACCESS_TOKEN -f Dockerfile.test .


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment