A New Collection of Thoughtful Learning Apps — Now Available on iOS & Android

Image
I’m excited to share a set of mobile apps I’ve recently completed and published on both the Google Play Store and the Apple App Store. These apps are designed with a simple goal in mind: to make meaningful, structured content more accessible, whether you’re studying theology or improving your English vocabulary. 📱 Now Available on Both Platforms All apps are live and available for download: Google Play Developer Page: https://play.google.com/store/apps/dev?id=5835943159853189043 Apple App Store Developer Page: https://apps.apple.com/ca/developer/q-z-l-corp/id1888794100 📖 Theology & Confession Study Apps For those interested in Reformed theology and classical Christian teachings, I’ve developed a series of apps that present foundational texts in a clean, focused reading format: The Belgic Confession Canons of Dort Heidelberg Catechism Westminster Shorter Catechism Each app is designed to provide a distraction-free experience, making it easier to read, reflect, and revisit these im...

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

Fix “A problem occurred starting process 'command node'” in Android Studio for React Native

Fix up watchman issue with Ghost

Using Mutual TLS (mTLS) in Next.js (Server-Side Only)