The Ultimate Guide to Self-Hosting Next.js 16 (Docker + SQLite) - By Sourav Mishra (@souravvmishra)
Save big on cloud costs by self-hosting Next.js 16. Learn to deploy with Docker, SQLite (Turso/LibSQL), and Nginx on any VPS.
The "Vercel tax" is a hot topic, but the truth is you don't always need a managed platform. With Next.js 16's improved standalone output, self-hosting has never been easier or more reliable.
In this guide, I, Sourav Mishra, Co-founder of Codestam Technologies, will show you how to deploy a production-ready Next.js application using Docker and SQLite for a fraction of the cost, a strategy we use for many of our internal tools.
Why Self-Host?
Control and Cost. While Vercel provides an incredible DX, scaling a simple project can get expensive. A $5 VPS can easily handle thousands of requests per second if optimized correctly.
Step 1: The Dockerfile
The secret sauce is the multi-stage build. This keeps your image size small (~100MB instead of 1GB+).
# Dockerfile
FROM node:20-alpine AS base
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]
[!TIP] Make sure to set
output: 'standalone'in yournext.config.mjsfile for this to work.
Step 2: Database Strategy (SQLite)
For many apps, you don't need Postgres. SQLite (specifically LibSQL or Turso) is incredibly fast and file-based, making it perfect for single-container deployments.
At Codestam Technologies, we successfully served 50k+ requests/day on a single $5 VPS using this exact SQLite setup.
I recommend using Better-SQLite3 or LibSQL locally.
// lib/db.ts
import Database from 'better-sqlite3';
const db = new Database('sqlite.db');
db.pragma('journal_mode = WAL');
export default db;
When deploying, simply mount a volume to persist your sqlite.db file.
Step 3: Docker Composition
Use docker-compose.yml to orchestrate your container and ensure restarts.
version: '3'
services:
nextjs-app:
build: .
restart: always
ports:
- "3000:3000"
volumes:
- ./data:/app/data
Step 4: Reverse Proxy with Nginx
Never expose Node.js directly to the internet. Use Nginx to handle SSL and request forwarding.
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Comparison: Vercel vs. Self-Hosted
| Feature | Vercel | Self-Hosted (Docker) |
|---|---|---|
| Cost | Free tier, then $20/mo/seat + usage | Fixed ($5-$10/mo total) |
| Setup | Zero-config | Moderate (Docker, Nginx) |
| Scalability | Serverless / Edge | Vertical (Upgrade VPS) |
| Control | Limited | Full Root Access |
Conclusion
Self-hosting Next.js 16 is a viable, cost-effective strategy for many businesses. By leveraging Docker and SQLite, you get a robust architecture that you own 100%.
If you are worried about search rankings on your self-hosted site, read my guide on Next.js SEO Mastery.
For more deployment tips, check out my previous guide on Cache Components or see how Turbopack speeds up your dev workflow.
This guide was written by Sourav Mishra, Co-founder of Codestam Technologies and a Full Stack Engineer specializing in Devops and Next.js.