Next.js doesn't require Vercel. Any container platform — Docker, Kubernetes, Fly.io, Railway, Render — can run a Next.js app.
1const config: NextConfig = {
2 output: "standalone",
3};With output: "standalone", next build produces a self-contained .next/standalone folder including a minimal node_modules — perfect for Docker.
1# 1. Install deps with a lockfile-only layer for cacheability
2FROM node:20-alpine AS deps
3WORKDIR /app
4COPY package.json package-lock.json* ./
5RUN npm ci
6
7# 2. Build the app
8FROM node:20-alpine AS builder
9WORKDIR /app
10COPY /app/node_modules ./node_modules
11COPY . .
12ENV NEXT_TELEMETRY_DISABLED 1
13RUN npm run build
14
15# 3. Run the slim production image
16FROM node:20-alpine AS runner
17WORKDIR /app
18ENV NODE_ENV production
19ENV NEXT_TELEMETRY_DISABLED 1
20
21RUN addgroup --system --gid 1001 nodejs
22RUN adduser --system --uid 1001 nextjs
23
24COPY /app/public ./public
25COPY /app/.next/standalone ./
26COPY /app/.next/static ./.next/static
27
28USER nextjs
29EXPOSE 3000
30ENV PORT 3000
31ENV HOSTNAME "0.0.0.0"
32
33CMD ["node", "server.js"]node_modules
.next
.git
.env.local
.env.development
README.md
tests
*.log
1docker build -t my-app .
2docker run -p 3000:3000 \
3 -e DATABASE_URL=postgres://... \
4 -e SESSION_SECRET=... \
5 my-app1services:
2 web:
3 build: .
4 ports: ["3000:3000"]
5 environment:
6 DATABASE_URL: postgresql://postgres:secret@db:5432/myapp
7 SESSION_SECRET: dev-secret-not-for-prod
8 depends_on: [db]
9
10 db:
11 image: postgres:16-alpine
12 environment:
13 POSTGRES_PASSWORD: secret
14 POSTGRES_DB: myapp
15 volumes: ["db:/var/lib/postgresql/data"]
16
17volumes:
18 db:docker compose up boots the whole stack.
The default cache uses the local filesystem — fine for single-replica, problematic for horizontal scaling. For multi-instance:
1// next.config.ts
2const config: NextConfig = {
3 cacheHandler: require.resolve("./cache-handler.js"),
4 cacheMaxMemorySize: 0,
5};Implement a Redis-backed cache handler — examples in the Next.js docs.
The default image optimizer requires sharp. node:20-alpine ships musl libc — install vips-dev build tools if you hit issues:
1FROM node:20-alpine
2RUN apk add --no-cache vips-devOr set images: { unoptimized: true } and let your CDN (Cloudflare, CloudFront, Imgix) handle resizing.
1server {
2 listen 80;
3 server_name yourapp.com;
4
5 location / {
6 proxy_pass http://localhost:3000;
7 proxy_set_header Host \$host;
8 proxy_set_header X-Real-IP \$remote_addr;
9 proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
10 proxy_set_header X-Forwarded-Proto \$scheme;
11 }
12}Add Let's Encrypt with Certbot, you're production-ready.
| Vercel | Self-Hosted | |
|---|---|---|
| Time to first deploy | 5 min | 1–2 hours |
| Edge network | Worldwide | Wherever you deploy |
| ISR / cache | Built-in | Bring your own |
| Image optimization | Free | sharp install needed |
| Cost at scale | $$$ | $ (raw compute) |
| Lock-in | Some | None |
For most startups, Vercel until ~$1k/month in bills, then evaluate. For enterprises with regulated workloads, self-host from day one.