Skip to content

Next.js on a VPS: the boring production checklist

2 min readInfrastructure · Docker · Nginx1 view

caddy / nginx :443next :3000containerdokployrestartsubuntu vps · docker · cloudflaressh keys only · ufw · backups

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 export in a shell you will forget — systemd EnvironmentFile, 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-stopped or Restart=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; local curl proves 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.

Shihab Hossain — full-stack developer portrait

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

All notes