Deploy NodeJS bằng Docker Compose trên VPS: hướng dẫn thực chiến từng bước
Deploy app NodeJS lên VPS nghe đơn giản: build app, chạy process, mở port. Thực tế hơn nhiều: env, network, persist data, restart policy, reverse proxy, SSL, log, zero-downtime tương đối. Làm thiếu 1 mắt xích → app chạy local ngon, lên server lỗi ngay.
Bài này đi theo hướng thực chiến: từ một app NodeJS bất kỳ, đóng gói bằng Docker, orchestration bằng Docker Compose, đưa lên VPS, chạy sau Nginx reverse proxy. Mục tiêu: dễ lặp lại, dễ bảo trì, ít lỗi môi trường.
Vì sao nên dùng Docker Compose trên VPS?
Nếu deploy NodeJS “thủ công”, flow thường là:
– cài Node
– pull code
– npm install
– chạy pm2 hoặc node server.js
– cấu hình Nginx riêng
– cập nhật bản mới → dễ lệch môi trường
Docker Compose giải quyết khá gọn:
– Đồng nhất môi trường → local/server giống nhau
– Tách service rõ ràng → app, db, redis, nginx
– Khởi động lại dễ → docker compose up -d
– Rollback dễ hơn → quay lại image/cấu hình cũ
– Onboarding nhanh → dev khác chỉ cần Docker
Compose đặc biệt hợp cho: – app nhỏ-vừa – 1 VPS – chưa cần Kubernetes – muốn quy trình triển khai rõ, ít overhead
Kiến trúc triển khai đề xuất
Một mô hình phổ biến:
– NodeJS app → chạy trong container – Nginx → reverse proxy, nhận request từ internet – Docker network → app/nginx nói chuyện nội bộ – VPS → Ubuntu 22.04 hoặc tương đương – Domain → trỏ về IP VPS – SSL → cấu hình sau khi app chạy ổn
Luồng request:
– User → domain – domain → VPS – Nginx container → forward sang Node app container – Node app → trả response
Nếu có DB như PostgreSQL/MySQL, có 2 hướng:
– DB nằm ngoài VPS/container → ổn hơn cho production – DB chạy cùng Compose → tiện, nhanh, nhưng cần backup kỹ
Bước 1: Chuẩn bị VPS
Trên VPS mới, cập nhật hệ thống trước:
sudo apt update && sudo apt upgrade -yCài Docker:
curl -fsSL https://get.docker.com | shCho user hiện tại dùng Docker không cần sudo:
sudo usermod -aG docker $USER
newgrp dockerKiểm tra:
docker --version
docker compose versionMở firewall cơ bản:
sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enableBước 2: Chuẩn bị ứng dụng NodeJS
Giả sử app có cấu trúc:
app/
├── src/
├── package.json
├── package-lock.json
└── server.jsApp cần lắng nghe trên 0.0.0.0, không phải localhost. Ví dụ:
app.listen(process.env.PORT || 3000, '0.0.0.0', () => {
console.log('Server running');
});Nếu bind localhost → container ngoài không truy cập được.
Tạo file .env:
PORT=3000
NODE_ENV=productionKhông hard-code secret trong code. Dùng env.
Bước 3: Viết Dockerfile tối ưu cho production
Tạo Dockerfile:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Ý nghĩa:
– node:20-alpine → image nhẹ
– WORKDIR /app → thư mục làm việc
– copy package*.json trước → tận dụng cache layer
– npm ci --omit=dev → cài đúng lockfile, bỏ devDependencies
– EXPOSE 3000 → mô tả cổng app dùng
– CMD → lệnh chạy chính
Tạo thêm .dockerignore:
node_modules
npm-debug.log
.git
.env
Dockerfile
docker-compose.yml.dockerignore tốt → build nhanh hơn, image sạch hơn.
Bước 4: Tạo Docker Compose
Tạo docker-compose.yml:
version: '3.9'
services:
app:
build: .
container_name: node_app
restart: always
env_file:
- .env
expose:
- "3000"
networks:
- app_network
nginx:
image: nginx:alpine
container_name: nginx_proxy
restart: always
ports:
- "80:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- app
networks:
- app_network
networks:
app_network:
driver: bridge
Điểm quan trọng:
– restart: always → VPS reboot, container tự lên
– expose thay vì ports cho app → app không public trực tiếp
– Nginx mới là service public cổng 80
– 2 container chung app_network → gọi nhau qua service name
Bước 5: Cấu hình Nginx reverse proxy
Tạo file nginx/default.conf:
server {
listen 80;
server_name your-domain.com www.your-domain.com;
location / {
proxy_pass http://app: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;
}
}
Ở đây:
– proxy_pass http://app:3000; → app là tên service trong Compose
– header forward chuẩn → app biết IP thật, protocol thật
Nếu app có upload/file lớn, thêm:
client_max_body_size 20M;Bước 6: Upload code lên VPS
Có nhiều cách:
– git clone
– scp
– CI/CD push trực tiếp
Cách đơn giản:
git clone <repo-url>
cd <project-folder>Kiểm tra lại file:
– Dockerfile
– docker-compose.yml
– .env
– nginx/default.conf
Rồi build + chạy:
docker compose up -d --buildKiểm tra container:
docker psXem log:
docker compose logs -fNếu lỗi, log thường chỉ đúng chỗ: app crash, thiếu env, sai port, Nginx sai upstream.
Bước 7: Trỏ domain về VPS
Trong DNS provider, tạo:
– A record → your-domain.com → IP VPS
– A record → www.your-domain.com → IP VPS
Chờ DNS propagate vài phút đến vài giờ.
Test:
ping your-domain.comHoặc mở trình duyệt:
– http://your-domain.com
Nếu thấy app → phần deploy cơ bản đã xong.
Bước 8: Thêm SSL cho production
Chạy HTTP thôi chưa đủ. Production nên có HTTPS.
Có 2 hướng:
– Certbot trên host – Nginx Proxy Manager / Traefik / Caddy
Với mô hình đơn giản, có thể cài Certbot ngay trên VPS host rồi mount cert vào Nginx. Tuy nhiên cách này cần thêm cấu hình. Nếu muốn tối giản hơn, nhiều team chọn Caddy hoặc Traefik vì tự cấp SSL.
Nếu vẫn dùng Nginx thuần, sau khi có cert, config sẽ có:
– server 80 → redirect 443
– server 443 → dùng ssl_certificate, ssl_certificate_key
Điểm mấu chốt: đừng xem SSL là bước “để sau rất lâu”. App public không có HTTPS → kém an toàn, browser cảnh báo.
Bước 9: Cập nhật phiên bản mới
Khi có code mới:
git pull
docker compose up -d --buildFlow này đủ cho đa số app nhỏ-vừa.
Nếu muốn dọn image cũ:
docker image prune -aNếu cần restart nhanh:
docker compose restartNếu muốn dừng toàn bộ:
docker compose downCác lỗi thực tế rất hay gặp
App chạy local, lên VPS lỗi 502
Thường do:
– app không listen 0.0.0.0
– sai port
– container app crash
– Nginx proxy nhầm host/port
Check:
docker compose logs app
docker compose logs nginxEnv không load
Nguyên nhân:
– file .env thiếu biến
– sai tên biến
– app đọc env trước khi config đúng
Fix:
– kiểm tra env_file
– log thử process.env.PORT
Build chậm, image nặng
Nguyên nhân:
– copy cả node_modules
– không có .dockerignore
– dùng image base quá lớn
Fix:
– thêm .dockerignore
– dùng node:alpine
– tối ưu layer build
Mất dữ liệu khi redeploy
Nếu có DB trong container mà không mount volume → down/recreate dễ mất data.
Fix: – dùng named volume – hoặc tách DB sang managed service
Một số mẹo production đáng giá
– Dùng healthcheck → Compose biết container còn khỏe không
– Giới hạn log → tránh đầy disk
– Backup định kỳ → nhất là DB, uploads
– Không chạy app bằng root nếu có thể
– Tách .env production khỏi code repo
– Dùng tag image cố định thay vì latest trong môi trường quan trọng
– Giám sát CPU/RAM/disk → VPS nhỏ rất dễ đầy ổ vì log/image
Compose không phải “chuẩn cuối cùng”, nhưng với 1 VPS, nó là điểm cân bằng rất tốt giữa đơn giản và đủ chuyên nghiệp.
Kết luận
Deploy NodeJS bằng Docker Compose trên VPS là một lựa chọn cực thực dụng: nhanh setup, dễ lặp lại, ít lệch môi trường, phù hợp production quy mô nhỏ-vừa. Quy trình cốt lõi chỉ gồm vài khối:
– đóng gói app bằng Dockerfile
– ghép service bằng docker-compose.yml
– đặt Nginx làm reverse proxy
– trỏ domain, thêm SSL
– dùng log + restart policy để vận hành ổn định
Điều quan trọng nhất không phải “chạy được”, mà là chạy ổn định sau nhiều lần cập nhật. Khi bạn có thể git pull → docker compose up -d --build và hệ thống vẫn lên mượt, lúc đó quy trình deploy mới thật sự trưởng thành.
Nếu mới bắt đầu, hãy triển khai trước với 1 app NodeJS + 1 Nginx container. Khi quen rồi, bạn có thể mở rộng thêm PostgreSQL, Redis, CI/CD, auto SSL, monitoring. Đi từng bước như vậy → ít lỗi, dễ kiểm soát, sát thực tế hơn nhiều.
Bình luận (0)
Chưa có bình luận. Hãy là người đầu tiên!