Building a Minimal Docker Image for Next.js Standalone Apps

Building a Minimal Docker Image for Next.js Standalone Apps

This is Part 2 of the series: Self-Hosting Next.js in Kubernetes (Without Vercel).

In Part 1, we covered how to run a Next.js app in standalone mode. Now let’s improve the deployment by building a clean, minimal Docker image that works well in Kubernetes and OpenShift.




Why Multi-Stage Docker Builds Matter

A common mistake is shipping your entire source tree and development dependencies into production.

A multi-stage Docker build allows you to:

  • Keep build tools out of production
  • Reduce image size significantly
  • Improve security
  • Speed up CI and deployments

For Next.js standalone apps, this pattern works extremely well.


Stage 1: Builder Image

The builder stage installs dependencies and runs the normal Next.js build. Nothing special happens here.

FROM node:22-alpine AS builder

WORKDIR /build

COPY package.json package-lock.json ./
RUN npm ci

COPY . .
RUN npm run build

At this point, Next.js produces:

  • .next/standalone
  • .next/static
  • public

Stage 2: Prepare Standalone Assets

Standalone mode does not automatically include static assets. We must copy them manually.

RUN cp -R dist/apps/example-web/.next/static \
  dist/apps/example-web/.next/standalone/dist/apps/example-web/.next/static

RUN cp -R dist/apps/example-web/public \
  dist/apps/example-web/.next/standalone/apps/example-web/public

This ensures:

  • /_next/static is served correctly
  • /public assets work as expected

Stage 3: Runtime Image

Now we copy only what we need into a lightweight runtime image.

FROM image-registry.openshift-image-registry.svc:5000/internal/nodejs-runtime:22

WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

USER root
COPY --from=builder /build/dist/apps/example-web/.next/standalone ./

RUN chown -R 1001:0 /app && chmod -R g+rwx /app

USER 1001

EXPOSE 3000
ENV PORT=3000

CMD NODE_EXTRA_CA_CERTS=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem \
node apps/example-web/server.js

What This Image Contains (and What It Doesn’t)

Included:

  • Minimal Node.js runtime
  • Standalone Next.js server
  • Required dependencies only

Excluded:

  • Source code
  • Dev dependencies
  • Build tools
  • Unused node_modules

This is exactly what you want in production.


Why This Works Well in OpenShift

  • Runs as a non-root user
  • Compatible with restricted SCCs
  • No writable root filesystem required
  • Predictable startup behavior

It also works the same way in:

  • Vanilla Kubernetes
  • Private clusters
  • On-prem environments

Series Progress

Self-Hosting Next.js in Kubernetes (Without Vercel)


Final Thoughts

Standalone mode is only half the story. A clean Docker image is what makes Next.js production-ready in Kubernetes.

In the next post, we’ll wire this image into a real OpenShift deployment using Deployment, Service, and Route objects.

❤️ Support This Blog


If this post helped you, you can support my writing with a small donation. Thank you for reading.


Comments

Popular posts from this blog

fixed: embedded-redis: Unable to run on macOS Sonoma

Copying MDC Context Map in Web Clients: A Comprehensive Guide

Reset user password for your own Ghost blog