Next.js on a VPS: the boring production checklist
2 min readInfrastructure · Docker · Nginx1 view
Platforms like Vercel are the right default for a lot of projects — including this one. But a real chunk of freelance work ends on a server the client already owns: a Hetzner or OVH box, an Ubuntu invoice someone already paid. When that is the constraint, taste shows up less in exotic tooling and more in a checklist you don't skip.
Package it or regret it
The app should ship as one Docker image. With output: 'standalone' in next.config.ts, a multi-stage build keeps the runtime image small and the deploy script trivial:
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine AS runner
WORKDIR /app
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]Reverse proxy and TLS — pick once
- Caddy if you want automatic HTTPS with a two-line config — hard to get wrong, which is the point.
- Nginx if the box already runs it or the client has opinions; pair with certbot and a renewal timer you actually verify.
- Cloudflare in front when DNS, caching and a basic WAF are wanted — but don't proxy blindly; tune the SSL mode to the origin and know what it caches.
- Dokploy is a reasonable middle ground when a single box should feel like a small Platform-as-a-Service — deploys, SSL and logs without hand-rolling systemd units.
The boring essentials nobody skips twice
- Environment via files, not
exportin a shell you will forget — systemdEnvironmentFile, Docker--env-file, or the platform's env UI. - A firewall that denies everything except 22/80/443, and SSH keys only.
- Logs to journald or a file you rotate; an app you can't see is an app you can't run.
- A restart policy (
--restart unless-stoppedorRestart=always) and one supervised update cadence. - Backups of whatever matters — and a restore you have actually tried once.
- An uptime check pinging
/from outside the box; localcurlproves nothing.
So when is Kubernetes actually the right call?
Not for one marketing site or a small storefront. Kubernetes starts earning its keep when you have several services that deploy independently, need rolling releases and health-gated rollouts, or a team that has outgrown a single box's runbook. For everything smaller, a single VPS with Docker and a disciplined checklist is cheaper, calmer and easier to hand over. Choose the boring thing — then instrument it well.

Written by
Shihab Hossain — Full-Stack Developer
I build thoughtful digital products — from fast web experiences to mobile apps and the infrastructure behind them.
Work with me