# =========================================
# Stage 1: Install Dependencies
# =========================================
# IMPORTANT: Node.js Version Maintenance
# This Dockerfile uses Node.js 24.13.0-slim, which was the latest LTS version at the time of writing.
# To ensure security and compatibility, regularly update the NODE_VERSION ARG to the latest LTS version.
ARG NODE_VERSION=24.13.0-slim
FROM node:${NODE_VERSION} AS dependencies
# Set the working directory
WORKDIR /app
# Copy package-related files first to leverage Docker's caching mechanism
COPY package.json package-lock.json* yarn.lock* pnpm-lock.yaml* ./
# Install project dependencies with frozen lockfile for reproducible builds
RUN --mount=type=cache,target=/root/.npm \
--mount=type=cache,target=/usr/local/share/.cache/yarn \
--mount=type=cache,target=/root/.local/share/pnpm/store \
if [ -f package-lock.json ]; then \
npm ci --no-audit --no-fund; \
elif [ -f yarn.lock ]; then \
corepack enable yarn && yarn install --frozen-lockfile --production=false; \
elif [ -f pnpm-lock.yaml ]; then \
corepack enable pnpm && pnpm install --frozen-lockfile; \
else \
echo "No lockfile found." && exit 1; \
fi
# ============================================
# Stage 2: Build Next.js Application
# ============================================
FROM node:${NODE_VERSION} AS builder
# Set the working directory
WORKDIR /app
# Copy project dependencies from dependencies stage
COPY --from=dependencies /app/node_modules ./node_modules
# Copy application source code
COPY . .
ENV NODE_ENV=production
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED=1
# Build Next.js application
# Cache mount speeds up subsequent builds by persisting Next.js build cache
RUN --mount=type=cache,target=/app/.next/cache \
if [ -f package-lock.json ]; then \
npm run build; \
elif [ -f yarn.lock ]; then \
corepack enable yarn && yarn build; \
elif [ -f pnpm-lock.yaml ]; then \
corepack enable pnpm && pnpm build; \
else \
echo "No lockfile found." && exit 1; \
fi
# =========================================
# Stage 3: Serve Static Files with serve
# =========================================
FROM node:${NODE_VERSION} AS runner
# Set the working directory
WORKDIR /app
# Install serve globally (pinned version for reproducibility, update to the latest version as needed)
# Cache npm global installs to speed up builds
RUN --mount=type=cache,target=/root/.npm \
npm install -g serve@14.2.5
# Set the port for serve (default is 3000)
ENV PORT=3000
# Copy the static build output from the build stage
COPY --from=builder --chown=node:node /app/out ./out
# Use the built-in non-root user for security best practices
USER node
# Expose port 3000 to allow HTTP traffic
EXPOSE 3000
# Start serve to serve static files
# -s: serve single-page application (SPA) mode
# -l: listen on specified port
# -n: no clipper (don't show file listing)
CMD ["serve", "-s", "out", "-l", "3000", "-n"]