HostingRank AI
Tutorial

Deploy Next.js on a VPS (Docker + Nginx + PM2)

Deploy a Next.js app to a DigitalOcean or Vultr VPS with Docker and Nginx reverse proxy, including HTTPS via Caddy and zero-downtime deploys.

Updated: 2026-07-15·12 min read

Why run Next.js on a VPS?

Platforms like Vercel are the fastest way to ship, but a VPS gives you full control, predictable pricing, and room to grow past free-tier limits. A 2GB DigitalOcean or Vultr instance (~₹400-900/mo) comfortably runs a production Next.js app.

In our benchmarks, Vultr and DigitalOcean both deliver a global TTFB around 150-250ms for a cached Next.js static route — comparable to managed platforms at a fraction of the cost.

What you'll need

  • A VPS (2GB RAM minimum, Ubuntu 24.04) — see our DigitalOcean vs Vultr guide
  • A domain name
  • Node.js 18+ on your local machine
  • A Docker Hub or GHCR account (optional, for registry deploys)

The Dockerfile

FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production NEXT_TELEMETRY_DISABLED=1
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]

Make sure output: "standalone" is set in your next.config.js.

docker-compose.yml

services:
  web:
    build: .
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"

The app binds to localhost only — the reverse proxy is the only public entry point.

One command with Caddy

# caddy/Caddyfile
example.com {
    reverse_proxy web:3000
}
services:
  web:
    build: .
    restart: unless-stopped
    expose:
      - "3000"
  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
volumes:
  caddy_data:

Run docker compose up -d --build and Caddy issues the SSL certificate automatically on first request.

Zero-downtime deploys

  1. Build and push your image.
  2. On the server: docker compose pull && docker compose up -d.
  3. Compose restarts only the changed container; the healthcheck gate keeps traffic flowing.

Next steps

  • Add fail2ban and set up automatic apt security updates.
  • Use a managed Postgres or a separate container for your database.
  • Follow our hardening guide before going to production.

Get the monthly benchmark report

New TTFB and uptime data, price drops, and hosting deals. No spam.

More tutorials