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
- Build and push your image.
- On the server:
docker compose pull && docker compose up -d. - Compose restarts only the changed container; the healthcheck gate keeps traffic flowing.
Next steps
- Add fail2ban and set up automatic
aptsecurity updates. - Use a managed Postgres or a separate container for your database.
- Follow our hardening guide before going to production.