Hướng dẫn chạy Next.js trên VPS với Docker: Dễ quản lý, dễ mở rộng
Triển khai Next.js lên VPS thường bắt đầu khá đơn giản: cài Node.js, clone source, chạy npm install, build, dùng PM2. Nhưng khi dự án lớn hơn, nhiều môi trường hơn, nhiều service hơn — database, Redis, reverse proxy, SSL — cách làm thủ công bắt đầu khó kiểm soát.
Docker giải quyết vấn đề đó bằng cách đóng gói ứng dụng cùng môi trường chạy. Thay vì phụ thuộc vào VPS đang cài Node version nào, package nào, biến môi trường nào, ta định nghĩa tất cả trong Dockerfile và docker-compose.yml. Kết quả: deploy ổn định hơn, rollback dễ hơn, mở rộng thuận tiện hơn.
Bài viết này hướng dẫn cách chạy Next.js trên VPS bằng Docker theo hướng thực tế, dễ bảo trì.
Vì sao nên chạy Next.js bằng Docker trên VPS?
Môi trường nhất quán
Next.js phụ thuộc khá nhiều vào phiên bản Node.js, package manager, biến môi trường, build output. Nếu dev dùng Node 20 nhưng VPS dùng Node 18, lỗi có thể xuất hiện.
Docker giúp cố định môi trường:
– Node.js version cụ thể – Lệnh build rõ ràng – Runtime tách biệt – Không “bẩn” hệ thống VPS
Code chạy được trong container local → gần như chắc chắn chạy được trên VPS.Deploy dễ hơn
Thay vì cài thủ công nhiều thứ trên server, bạn chỉ cần:
docker compose up -d --buildMuốn restart:
docker compose restartMuốn xem log:
docker compose logs -fRất gọn.
Dễ mở rộng
Khi cần thêm PostgreSQL, Redis, Nginx, worker, queue, bạn chỉ cần bổ sung service trong docker-compose.yml.
Ví dụ:
– app: Next.js
– db: PostgreSQL
– redis: cache/session
– nginx: reverse proxy
– watchtower: auto update image
Kiến trúc rõ ràng → quản lý dễ.
Chuẩn bị VPS
Bạn cần một VPS Linux, thường dùng Ubuntu 22.04 hoặc 24.04.
Yêu cầu tối thiểu:
– RAM: 1GB cho app nhỏ, nên 2GB+ – CPU: 1 vCPU trở lên – Disk: 20GB+ – Domain trỏ về IP VPS – SSH access
Cập nhật hệ thống:
sudo apt update && sudo apt upgrade -yCài Docker:
curl -fsSL https://get.docker.com | shThêm user hiện tại vào group Docker:
sudo usermod -aG docker $USERĐăng xuất SSH rồi đăng nhập lại.
Kiểm tra:
docker --version
docker compose versionCấu trúc project Next.js
Ví dụ project:
my-next-app/
├── app/
├── public/
├── package.json
├── next.config.js
├── Dockerfile
├── docker-compose.yml
└── .dockerignoreNếu dùng Next.js App Router hoặc Pages Router đều được. Điểm quan trọng là build ra production server.
Trong package.json, cần có scripts:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}Tạo .dockerignore
File này giúp Docker không copy những thứ không cần thiết vào image.
node_modules
.next
.git
Dockerfile
docker-compose.yml
npm-debug.log
.env
.env.localLưu ý: không copy .env vào image. Nên truyền biến môi trường qua Docker Compose hoặc secret.
Viết Dockerfile cho Next.js
Dùng multi-stage build để image nhẹ hơn.
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/next.config.js ./next.config.js
EXPOSE 3000
CMD ["npm", "start"]
Cách này đơn giản, dễ hiểu. Tuy image chưa tối ưu tối đa, nhưng phù hợp nhiều dự án.
Nếu muốn nhẹ hơn nữa, có thể dùng output: 'standalone' trong next.config.js.
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone'
}
module.exports = nextConfig
Khi dùng standalone, Dockerfile sẽ khác một chút, image nhỏ hơn, runtime ít dependency hơn.
Tạo docker-compose.yml
Cấu hình cơ bản:
services:
next-app:
build:
context: .
dockerfile: Dockerfile
container_name: next-app
restart: unless-stopped
ports:
- "3000:3000"
environment:
NODE_ENV: production
NEXT_PUBLIC_SITE_URL: https://example.comChạy app:
docker compose up -d --buildKiểm tra container:
docker psXem log:
docker compose logs -f next-appNếu mọi thứ ổn, app sẽ chạy tại:
http://your-server-ip:3000Dùng Nginx reverse proxy
Không nên public trực tiếp port 3000 cho production. Nên dùng Nginx đứng trước app để:
– Gắn domain – Cài SSL – Proxy request – Nén gzip/brotli – Quản lý nhiều app trên cùng VPS
Cài Nginx:
sudo apt install nginx -yTạo config:
sudo nano /etc/nginx/sites-available/next-appNội dung:
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Kích hoạt:
sudo ln -s /etc/nginx/sites-available/next-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxLúc này domain sẽ trỏ vào app Docker.
Cài SSL với Certbot
SSL gần như bắt buộc cho production.
Cài Certbot:
sudo apt install certbot python3-certbot-nginx -yCấp chứng chỉ:
sudo certbot --nginx -d example.com -d www.example.comKiểm tra auto renew:
sudo certbot renew --dry-runSau bước này, website chạy qua HTTPS.
Quản lý biến môi trường
Không hardcode key trong source. Dùng file .env.production trên VPS hoặc khai báo trong docker-compose.yml.
Ví dụ:
services:
next-app:
build: .
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
env_file:
- .env.productionFile .env.production:
NODE_ENV=production
NEXT_PUBLIC_SITE_URL=https://example.com
DATABASE_URL=postgresql://user:password@db:5432/appdbLưu ý:
– Biến bắt đầu bằng NEXT_PUBLIC_ sẽ xuất hiện phía client
– Secret như DATABASE_URL, JWT_SECRET, API_SECRET không dùng prefix này
– Không commit .env.production lên Git
Thêm PostgreSQL bằng Docker Compose
Nếu app cần database, thêm service db.
services:
next-app:
build: .
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
env_file:
- .env.production
depends_on:
- db
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD: strongpassword
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Volume postgres_data giúp dữ liệu không mất khi container bị xóa.
Khi deploy thực tế, hãy dùng password mạnh, backup định kỳ, hạn chế mở port DB ra ngoài.
Quy trình deploy thực tế
Một quy trình đơn giản:
cd /var/www/my-next-app
git pull origin main
docker compose up -d --build
docker image prune -fNếu dùng migration database:
docker compose exec next-app npx prisma migrate deployHoặc chạy migration trước khi restart app, tùy kiến trúc.
Có thể tạo script deploy.sh:
#!/bin/bash
set -e
git pull origin main
docker compose up -d --build
docker image prune -f
Phân quyền:
chmod +x deploy.shChạy:
./deploy.shGiám sát log và xử lý lỗi
Xem log realtime:
docker compose logs -fXem riêng app:
docker compose logs -f next-appRestart app:
docker compose restart next-appVào trong container:
docker compose exec next-app shKiểm tra tài nguyên:
docker statsMột số lỗi thường gặp:
Port đã bị chiếm
Bind for 0.0.0.0:3000 failed: port is already allocatedCách xử lý: kiểm tra process/container đang dùng port.
sudo lsof -i :3000
docker psBuild lỗi do thiếu env
Next.js có thể cần env ngay lúc build. Khi đó cần truyền env trong build stage hoặc đảm bảo biến tồn tại trước khi npm run build.
App chạy local nhưng lỗi trên VPS
Thường do:
– Sai biến môi trường – Node version khác – Database chưa sẵn sàng – Domain/API URL sai – Thiếu quyền file upload
Docker giảm phần lớn lỗi môi trường, nhưng không thay thế việc cấu hình đúng.
Tối ưu bảo mật cơ bản
Một vài việc nên làm ngay:
– Chỉ expose app qua localhost:
ports:
- "127.0.0.1:3000:3000"– Dùng Nginx làm reverse proxy – Bật firewall:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable– Không mở port PostgreSQL public
– Không commit .env
– Cập nhật image định kỳ
– Backup volume database
– Dùng SSH key thay password login
Khi nào cần mở rộng?
Với một VPS, Docker Compose đủ tốt cho nhiều website nhỏ và vừa. Khi traffic tăng, có thể mở rộng theo các hướng:
– Tăng cấu hình VPS – Tách database sang server riêng – Dùng Redis cache – Đưa static asset lên CDN/S3 – Chạy nhiều replica sau load balancer – Chuyển sang Kubernetes nếu hệ thống đủ lớn
Không nên phức tạp hóa sớm. Docker Compose là bước cân bằng tốt giữa đơn giản và chuyên nghiệp.
Kết luận
Chạy Next.js trên VPS với Docker giúp quá trình deploy rõ ràng, ổn định, dễ lặp lại. Bạn không còn phải phụ thuộc vào môi trường cài thủ công trên server. Mọi thứ — Node.js, build, runtime, database, biến môi trường — đều được định nghĩa bằng file cấu hình.
Cách triển khai thực tế nên gồm:
– Next.js chạy trong Docker container
– Nginx reverse proxy phía trước
– SSL bằng Certbot
– Env quản lý ngoài source
– Database dùng volume riêng
– Deploy qua docker compose up -d --build
– Log, backup, firewall đầy đủ
Với setup này, bạn có nền tảng đủ gọn cho dự án nhỏ, đủ chắc cho production, và đủ linh hoạt để mở rộng khi sản phẩm phát triển.
Bình luận (0)
Chưa có bình luận. Hãy là người đầu tiên!