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/staticpublic
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/staticis served correctly/publicassets 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)
- ✅ Part 1: Standalone Deployment
- ✅ Part 2: Minimal Docker Images
- ⏭ Part 3: OpenShift Deployment YAML
- ⏭ Part 4: Fixing Static Asset 404s
- ⏭ Part 5: Runtime Config & Secrets
- ⏭ Part 6: Health Checks & Scaling
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
Post a Comment