Vì sao nhiều team chọn VPS + PM2 + Nginx cho Next.js?
Deploy Next.js lên Vercel rất nhanh. Nhưng khi cần toàn quyền kiểm soát server, tối ưu chi phí, gắn nhiều service trên cùng máy, hoặc phải đáp ứng yêu cầu nội bộ về hạ tầng, mô hình VPS + PM2 + Nginx gần như là lựa chọn thực chiến nhất.
Bộ ba này phổ biến vì khá “chuẩn bài”:
– Next.js → app SSR/SSG/API. – PM2 → giữ tiến trình Node.js luôn sống, tự restart, log rõ ràng. – Nginx → reverse proxy, SSL, cache tĩnh, gzip, bảo vệ lớp ngoài.
Nếu cấu hình đúng, bạn sẽ có một stack vừa ổn định, vừa dễ vận hành, dễ mở rộng. Bài viết này đi theo hướng thực chiến: từ chuẩn bị VPS, build app, chạy bằng PM2, tới cấu hình Nginx “sạch”, ít lỗi, dễ bảo trì.
Kiến trúc triển khai chuẩn
Luồng cơ bản:
– User truy cập domain.
– Nginx nhận request ở cổng 80/443.
– Nginx reverse proxy vào app Next.js đang chạy ở 127.0.0.1:3000.
– PM2 quản lý tiến trình Next.js.
– SSL do Nginx xử lý.
Kiểu này có vài lợi ích rõ:
– Ẩn port app nội bộ → an toàn hơn. – Restart app không ảnh hưởng public endpoint nhiều. – Dễ thêm SSL, redirect, rate limit, cache. – Thuận tiện deploy nhiều app trên cùng VPS.
Chuẩn bị VPS trước khi deploy
Một VPS Ubuntu LTS là đủ. Tối thiểu:
– RAM: 1GB–2GB cho app nhỏ/vừa. – CPU: 1–2 vCPU. – OS: Ubuntu 22.04 LTS hoặc tương đương.
Cài các gói cần thiết:
sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx git curl ufwCài Node.js LTS. Nên dùng Node 18 hoặc 20:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejsKiểm tra:
node -v
npm -vCài PM2 global:
sudo npm install -g pm2Thiết lập firewall:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable– Tạo user riêng để deploy, không chạy app bằng root.
– Dùng SSH key thay vì password.
– Tách thư mục app rõ ràng, ví dụ: /var/www/my-next-app.
Deploy mã nguồn Next.js lên VPS
Clone source:
cd /var/www
sudo mkdir my-next-app
sudo chown -R $USER:$USER /var/www/my-next-app
cd /var/www/my-next-app
git clone <repo-url> .
Cài dependency:
npm installNếu app dùng biến môi trường, tạo file .env.production hoặc .env tùy cách bạn thiết kế:
nano .env.productionVí dụ:
NODE_ENV=production
PORT=3000
NEXT_PUBLIC_API_URL=https://api.example.comBuild app:
npm run buildChạy thử local trên VPS:
npm run startMặc định Next.js sẽ lắng nghe ở port 3000. Test nhanh:
curl http://127.0.0.1:3000Nếu app lên đúng, dừng tiến trình thử nghiệm bằng Ctrl + C.
Dùng PM2 để chạy Next.js ổn định
Điểm yếu lớn nhất khi chạy npm run start trực tiếp: thoát SSH hoặc app lỗi → process chết. PM2 xử lý đúng bài toán này.
Chạy app bằng PM2:
pm2 start npm --name "my-next-app" -- startKiểm tra:
pm2 list
pm2 logs my-next-appLưu cấu hình để server reboot vẫn tự chạy lại:
pm2 save
pm2 startupPM2 sẽ in ra một lệnh. Copy và chạy đúng lệnh đó.
Cách cấu hình PM2 “chuẩn hơn” bằng ecosystem file
Thay vì chạy lệnh rời, nên dùng file ecosystem.config.js:
module.exports = {
apps: [
{
name: "my-next-app",
script: "npm",
args: "start",
cwd: "/var/www/my-next-app",
instances: 1,
exec_mode: "fork",
env: {
NODE_ENV: "production",
PORT: 3000
}
}
]
};Chạy:
pm2 start ecosystem.config.js
pm2 saveƯu điểm:
– Dễ đọc. – Dễ version control. – Dễ scale sau này.
Một số lệnh PM2 nên nhớ
pm2 restart my-next-app
pm2 stop my-next-app
pm2 delete my-next-app
pm2 logs my-next-app
pm2 monitCấu hình Nginx reverse proxy đúng chuẩn
Tạo file config:
sudo nano /etc/nginx/sites-available/my-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";
proxy_cache_bypass $http_upgrade;
}
location /_next/static/ {
alias /var/www/my-next-app/.next/static/;
access_log off;
expires 30d;
add_header Cache-Control "public, immutable";
}
location /favicon.ico {
log_not_found off;
access_log off;
}
}
Kích hoạt site:
sudo ln -s /etc/nginx/sites-available/my-next-app /etc/nginx/sites-enabled/Kiểm tra syntax:
sudo nginx -tReload:
sudo systemctl reload nginxVì sao config này “thực chiến”?
– proxy_pass → forward req vào Next.js.
– X-Forwarded-* → giữ đúng IP, protocol, host.
– Upgrade/Connection → hỗ trợ websocket, hot path cần nâng cấp kết nối.
– /_next/static/ → Nginx phục vụ static asset trực tiếp, giảm tải cho Node.
– expires + immutable → tối ưu cache client rất tốt.
Gắn SSL bằng Let’s Encrypt
Sau khi domain đã trỏ A record về VPS, cài Certbot:
sudo apt install -y certbot python3-certbot-nginxCấp SSL:
sudo certbot --nginx -d example.com -d www.example.comCertbot thường tự sửa config Nginx cho bạn, gồm:
– redirect HTTP → HTTPS – khai báo certificate path – tự renew định kỳ
Test renew:
sudo certbot renew --dry-runSSL xong → app chuyên nghiệp hơn, bảo mật hơn, SEO tốt hơn.
Quy trình deploy cập nhật phiên bản mới
Khi có code mới, quy trình an toàn thường là:
1. SSH vào VPS. 2. Pull code mới. 3. Cài lại dependency nếu cần. 4. Build lại. 5. Restart PM2.
Lệnh mẫu:
cd /var/www/my-next-app
git pull origin main
npm install
npm run build
pm2 restart my-next-appNếu app có migration DB, hãy chèn bước migration trước restart.
Gợi ý giảm downtime
Với app lớn hơn, bạn có thể:
– dùng pm2 reload thay vì restart.
– bật nhiều instances nếu app không phụ thuộc memory local.
– cân nhắc CI/CD qua GitHub Actions.
Các lỗi phổ biến khi deploy Next.js trên VPS
1. Nginx báo 502 Bad Gateway
Nguyên nhân thường gặp:
– PM2 chưa chạy app.
– App chạy sai port.
– proxy_pass trỏ sai.
– Build lỗi nhưng chưa kiểm tra log.
Cách debug:
pm2 logs my-next-app
sudo journalctl -u nginx
sudo nginx -t2. App chạy local được, qua domain không lên
Khả năng cao:
– domain chưa trỏ đúng IP.
– firewall chưa mở 80/443.
– Nginx config chưa enable.
– quên reload Nginx.
3. Static file lỗi 404
Hay xảy ra khi:
– sai đường dẫn alias.
– build chưa tạo .next/static.
– deploy xong nhưng chưa npm run build.
4. Sau reboot server app không tự lên
Nguyên nhân:
– chưa chạy pm2 save.
– chưa cấu hình pm2 startup.
Một số tinh chỉnh nên có trong môi trường production
Để hệ thống “bền” hơn, nên bổ sung:
– log rotation cho PM2:
pm2 install pm2-logrotate– giới hạn upload/body trong Nginx nếu app có upload file. – gzip/brotli để giảm bandwidth. – fail2ban nếu VPS public internet. – monitoring: Netdata, Uptime Kuma, Grafana, hoặc ít nhất alert ping.
Nếu traffic tăng mạnh:
– VPS nhỏ → CPU/RAM nghẽn. – SSR nặng → response chậm. – DB remote → latency tăng.
Lúc đó cần tối ưu sâu hơn:
– cache layer. – giảm SSR không cần thiết. – dùng CDN cho ảnh/tĩnh. – scale app hoặc tách service.
Kết luận
Deploy Next.js trên VPS với PM2 + Nginx là mô hình rất đáng dùng khi bạn muốn chủ động hạ tầng, chi phí hợp lý, vận hành linh hoạt. Cấu hình chuẩn thực chiến không nằm ở việc “app chạy được”, mà ở chỗ:
– process sống ổn định với PM2 – request đi qua Nginx đúng chuẩn reverse proxy – static asset được phục vụ tối ưu – SSL đầy đủ – deploy/update đơn giản, dễ rollback, dễ debug
Nếu làm đúng ngay từ đầu, bạn sẽ tránh được phần lớn lỗi kinh điển như 502 Bad Gateway, app chết sau reboot, hay domain hoạt động chập chờn. Với team nhỏ hoặc sản phẩm đang tăng trưởng, đây là setup cân bằng rất tốt giữa đơn giản, kiểm soát, hiệu năng.
Muốn đi xa hơn, bạn có thể bổ sung CI/CD, Docker, monitoring và chiến lược zero-downtime. Nhưng nền móng vẫn là: Next.js chạy gọn trong PM2, Nginx đứng ngoài làm lớp bảo vệ và tối ưu truy cập. Đây là cấu hình đủ thực chiến để chạy production nghiêm túc.
Bình luận (0)
Chưa có bình luận. Hãy là người đầu tiên!