
Trả lời nhanh: Quy trình chuẩn deploy Next.js lên VPS: build bản production (next build), chạy tiến trình bằng PM2 để tự khởi động lại khi lỗi, đặt Caddy phía trước lo tên miền và HTTPS tự động. Cập nhật phiên bản mới chỉ là git pull && npm run build && pm2 restart. Toàn bộ mất khoảng 30 phút lần đầu, và bạn thoát khỏi giới hạn của các nền tảng serverless: không giới hạn thời gian chạy, không bill bất ngờ, chạy được cron và database ngay cùng máy.
Vercel deploy Next.js một chạm rất sướng, đến khi dự án lớn lên: hàm chạy quá thời gian bị chém, băng thông vượt gói giá nhảy vọt, muốn thêm database hay cron lại phải mua dịch vụ ngoài. Deploy lên VPS của chính mình là kỹ năng đáng có: chủ động mọi thứ, chi phí cố định. Bài này là playbook đầy đủ, dán lệnh theo thứ tự là chạy.
- Ba mảnh ghép: next build (sản phẩm) + PM2 (giữ tiến trình sống) + Caddy (tên miền, HTTPS tự động)
- Cập nhật = git pull, build lại, pm2 restart, gói gọn một script deploy.sh
- Ổ NVMe rút ngắn rõ hai bước nặng nhất: npm install và next build
- Cùng máy chạy luôn được Postgres, Redis, cron, thứ serverless bắt bạn mua ngoài
Bước 1: Chuẩn bị VPS và Node
Trên VPS Ubuntu mới (2 vCPU / 2 GB trở lên cho app nhỏ, 4-6 GB nếu kèm database):
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs git
node -v # can >= ban Node du an yeu cauKéo mã nguồn về và cài phụ thuộc:
git clone https://github.com/ban/du-an.git ~/app && cd ~/app
npm ci # dung npm ci thay npm install: cai dung theo lockfile, sach va nhanhĐây là bước đầu tiên bạn cảm nhận ổ đĩa: npm ci bung hàng vạn file nhỏ vào node_modules, trên ổ NVMe mất chừng nửa phút với dự án trung bình, trên ổ chậm có thể gấp nhiều lần.
Bước 2: Build và chạy thử
npm run build
npm run start # mac dinh cong 3000Mở http://IP-VPS:3000 kiểm tra nhanh (nhớ mở port tạm hoặc test bằng curl localhost:3000 ngay trên máy). Chạy được là phần khó nhất đã xong, hai bước còn lại chỉ là đóng gói cho tử tế.
Bước 3: PM2 giữ app sống 24/7
sudo npm i -g pm2
pm2 start npm --name app -- start
pm2 save && pm2 startup # chay lenh no in ra de tu khoi dong cung VPSTừ giờ app tự hồi sinh khi crash và tự chạy lại khi VPS reboot. Lệnh hay dùng: pm2 logs app xem log, pm2 restart app khởi động lại, pm2 monit nhìn RAM/CPU. Máy nhiều nhân muốn vắt hết CPU: pm2 start npm --name app -i max -- start chạy cluster mode.
Bước 4: Caddy - tên miền và HTTPS trong 5 dòng
Trỏ bản ghi A của tên miền về IP VPS, rồi:
sudo apt install -y caddy
# /etc/caddy/Caddyfile
ten-mien.vn {
reverse_proxy 127.0.0.1:3000
}
sudo systemctl reload caddyCaddy tự xin chứng chỉ HTTPS và tự gia hạn, không certbot, không cron SSL. Muốn thêm app thứ hai trên cùng VPS? Thêm một khối tên miền nữa trỏ vào port khác, xong.
Bước 5: Script deploy một lệnh
#!/bin/bash
# ~/deploy.sh: cap nhat phien ban moi
set -e
cd ~/app
git pull
npm ci
npm run build
pm2 restart app
echo "Deploy xong: $(date)"chmod +x ~/deploy.sh: từ nay mỗi lần lên code mới chỉ cần ./deploy.sh. Nâng cấp sau này: gọi script từ GitHub Actions qua SSH là có CI/CD; hoặc để Claude Code trên chính VPS vừa sửa code vừa tự deploy, quy trình một máy khép kín.
Lỗi hay gặp và cách gỡ
- Build chết vì thiếu RAM (thường trên gói 1-2 GB): thêm swap 2 GB hoặc build sẵn từ máy khác. Dấu hiệu: log dừng đột ngột, dmesg báo OOM.
- Trang 502: app chưa chạy (pm2 status), hoặc Caddy trỏ sai port.
- Đổi biến môi trường không ăn: Next.js đọc env lúc build với biến NEXT_PUBLIC_*, đổi env là phải build lại, không chỉ restart.
- Ảnh next/image lỗi: cấu hình images.domains/remotePatterns trong next.config theo đúng nguồn ảnh.
Tệp cấu hình đầy đủ, dán là chạy
Tệp quản lý tiến trình, đặt tên ecosystem.config.js trong thư mục dự án:
module.exports = {
apps: [{
name: "web",
script: "npm",
args: "start",
cwd: "/home/ban/app",
instances: "max", // dung het so nhan CPU
exec_mode: "cluster",
max_memory_restart: "700M",
env: { NODE_ENV: "production", PORT: 3000 },
error_file: "/home/ban/logs/web-err.log",
out_file: "/home/ban/logs/web-out.log",
time: true
}]
}pm2 start ecosystem.config.js pm2 save pm2 startup # in ra mot lenh, chay lenh do de app song sau reboot
Cấu hình máy chủ web phía trước, tệp Caddyfile:
ten-mien.vn {
encode zstd gzip
reverse_proxy 127.0.0.1:3000
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
}
}Chứng chỉ bảo mật được xin và gia hạn tự động, không phải làm gì thêm. Điều kiện: tên miền đã trỏ về IP máy chủ và cổng 80, 443 đang mở.
Kịch bản cập nhật một lệnh
#!/bin/bash # deploy.sh set -e cd /home/ban/app git pull origin main npm ci --omit=dev npm run build pm2 reload web --update-env # reload thay vi restart: khong dut ket noi pm2 save echo "Xong luc $(date)"
Dùng reload thay restart khi chạy ở chế độ cụm: các tiến trình được thay lần lượt nên người dùng không gặp lỗi trong lúc triển khai. Với chế độ một tiến trình thì hai lệnh như nhau.
Bốn lỗi hay gặp và cách gỡ
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
| Trang báo lỗi 502 | Ứng dụng chưa chạy hoặc sai cổng | pm2 list xem trạng thái, curl -I localhost:3000 kiểm trực tiếp |
| Bị dừng giữa lúc build, thông báo hết bộ nhớ | Máy chủ 1 tới 2 GB không đủ cho bước build | Thêm vùng nhớ tạm, hoặc build ở máy khác rồi chép kết quả sang |
| Ứng dụng chết sau khi đóng cửa sổ đăng nhập | Chạy bằng lệnh trực tiếp thay vì qua trình quản lý tiến trình | Dùng cấu hình ở trên, và nhớ chạy pm2 save |
| Ảnh không hiện sau khi triển khai | Thiếu thư viện xử lý ảnh, hoặc thiếu khai tên miền được phép | Cài thư viện xử lý ảnh, khai danh sách tên miền nguồn trong cấu hình dự án |
Ba việc bảo mật làm ngay sau khi lên máy chủ
- Đóng mọi cổng trừ 22, 80, 443. Ứng dụng chạy ở cổng 3000 chỉ nên nghe trên địa chỉ nội bộ, không phơi ra ngoài.
- Đổi sang đăng nhập bằng khóa và tắt đăng nhập bằng mật khẩu. Máy chủ mới dựng bị dò mật khẩu trong vòng vài giờ, đây là con số thật quan sát được trong nhật ký.
- Đưa tệp biến môi trường ra ngoài kho mã nguồn và đặt quyền đọc chỉ cho tài khoản chạy ứng dụng. Chuỗi kết nối cơ sở dữ liệu lọt lên kho mã nguồn là sự cố phổ biến bậc nhất.
Câu hỏi thường gặp
VPS cấu hình bao nhiêu thì chạy được Next.js?
App nhỏ chưa database: 2 vCPU / 2 GB chạy tốt (build hơi chậm). Kèm Postgres + Redis + traffic thật: 4-6 vCPU / 4-6 GB là điểm êm. Chi tiết theo quy mô xem bài Next.js 15 trên VPS ở mục liên quan.
Deploy VPS có mất tính năng gì của Vercel không?
Mất phần hào nhoáng: preview URL mỗi PR, edge network toàn cầu, analytics tích hợp. Khách chủ yếu ở Việt Nam thì edge toàn cầu gần như vô nghĩa, máy đặt tại VN còn nhanh hơn. Preview thì tự dựng bằng Cloudflare Tunnel khi cần.
Standalone output có nên bật không?
Nên với người muốn gọn: output standalone trong next.config giúp bản chạy production chỉ cần thư mục .next/standalone nhẹ hơn nhiều so với vác cả node_modules, tiện khi đóng Docker hoặc chuyển máy.
Một VPS chạy được mấy site Next.js?
Tùy RAM: mỗi app production chiếm cỡ 150-400 MB lúc nhàn. Gói 6-8 GB nuôi 3-5 site vừa thoải mái, Caddy điều phối tên miền, PM2 quản danh sách tiến trình, một máy làm cả farm.
Bài viết liên quan
- Next.js 15 chạy trên VPS: cấu hình từng quy mô
- Build Next.js chậm: nghẽn ở ổ đĩa hay CPU
- VPS ổ Enterprise U.2 NVMe RAID 10 tại TND
- VPS miễn phí có thật không? Rủi ro thật sự nằm ở đâu
- PM2 từ A-Z: giữ app Node sống 24/7 trên VPS
- Dùng proxy hợp pháp VN 2026: Luật BVDLCN 91/2025
- Cảnh báo CVE-2026-42945: Lỗ hổng Nginx mức CRITICAL (CVSS 9.2/10), VPS cần update khẩn cấp
- Tailscale Funnel lỗi 502, TLS handshake, cert: cách sửa
- Chọn model Ollama tiếng Việt 2026: Qwen, Gemma hay Llama?



