Deploy NodeJS bằng Docker trên VPS Ubuntu: quy trình chuẩn cho production
Deploy app NodeJS lên VPS nghe đơn giản: copy code, npm install, node server.js. Nhưng production khác hẳn. Chỉ cần 1 lần restart lỗi, 1 lần thiếu biến môi trường, 1 lần memory leak không bị giới hạn → downtime, log rối, rollback khó. Docker giải bài toán này khá gọn: môi trường chạy đồng nhất, build một lần, chạy nhiều nơi, dễ kiểm soát version, scale, rollback.
Nếu bạn đang dùng VPS Ubuntu để chạy API, web backend, cron worker hoặc app fullstack NodeJS, quy trình chuẩn nên xoay quanh 4 mục tiêu: ổn định, tái lập, an toàn, dễ vận hành. Bài viết này đi từ cấu trúc production thực tế: chuẩn bị VPS, viết Dockerfile tối ưu, dùng Docker Compose, reverse proxy bằng Nginx, SSL, logging, healthcheck, deploy/rollback.
Vì sao nên deploy NodeJS bằng Docker trên VPS?
NodeJS vốn phụ thuộc mạnh vào môi trường chạy: version Node, package native, biến môi trường, file build, process manager. Docker đóng gói toàn bộ phần đó thành image.
Lợi ích chính:
– Đồng nhất môi trường: local, staging, production gần như giống nhau. – Deploy nhanh: build image → chạy container mới. – Rollback dễ: quay lại image/tag cũ. – Cô lập tài nguyên: app lỗi ít kéo theo toàn server. – Dễ chuẩn hóa: app nào cũng theo cùng 1 flow.
Tuy nhiên, production không chỉ là “dockerize” app. Chuẩn hơn phải có:
– user deploy riêng – firewall – reverse proxy – HTTPS – volume/log hợp lý – restart policy – healthcheck – secret/env tách biệt – chiến lược cập nhật
Kiến trúc tối thiểu chuẩn production
Mô hình phổ biến:
– VPS Ubuntu
– Docker Engine + Docker Compose
– Container NodeJS app
– Nginx container hoặc Nginx host làm reverse proxy
– SSL từ Let’s Encrypt
– .env cho config
– Volume cho log/file cần lưu
– CI/CD hoặc deploy script để tự động hóa
Luồng request:
– Client → Nginx :80/:443 – Nginx → NodeJS container :3000 – Container restart tự động nếu app crash
Nếu app có DB, thường tách riêng: – DB managed service → tốt nhất cho production – Hoặc DB chạy container riêng → chấp nhận được với hệ nhỏ, nhưng backup phải rõ ràng
Bước 1: Chuẩn bị VPS Ubuntu đúng cách
Đừng bắt đầu bằng việc cài Docker ngay. Cần harden tối thiểu.
Việc nên làm đầu tiên
– Tạo user deploy, tránh dùng root hằng ngày
– Bật SSH key auth
– Tắt password login nếu có thể
– Cấu hình timezone
– Cập nhật package
– Bật firewall
Ví dụ:
sudo apt update && sudo apt upgrade -y
sudo apt install -y ufw curl git
sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enableNếu VPS chỉ phục vụ web:
– mở 22, 80, 443
– không mở port app nội bộ như 3000
Điểm quan trọng: container app không nên public trực tiếp ra Internet. Chỉ reverse proxy mới public.
Bước 2: Cài Docker, Compose
Trên Ubuntu, nên cài Docker từ repo chính thức để có version mới, ổn định hơn.
Sau khi cài:
– kiểm tra docker --version
– thêm user hiện tại vào group docker
– restart session
Lưu ý production: – bật Docker daemon khi boot – dọn image/container cũ định kỳ – không build rác liên tục trên VPS nhỏ
Bước 3: Viết Dockerfile đúng chuẩn production
Sai lầm phổ biến:
– dùng image quá nặng
– copy toàn bộ source thiếu .dockerignore
– chạy bằng root
– nhét cả dev dependency vào production image
Một Dockerfile tốt nên có các đặc điểm:
– base image rõ version, ví dụ node:20-alpine
– cài dependency trước để tận dụng cache
– chỉ copy file cần thiết
– build app nếu dùng TypeScript/NestJS/Next custom server
– chạy user không phải root
– expose đúng port nội bộ
Ví dụ cơ bản:
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/main.js"]
Ý nghĩa:
– multi-stage build → image nhỏ hơn
– npm ci → cài dependency đúng lockfile, ổn định hơn npm install
– --omit=dev → production image gọn
– USER node → giảm rủi ro bảo mật
Đừng quên .dockerignore:
node_modules
npm-debug.log
.git
.env
dist
Dockerfile
docker-compose.yml.env không nên copy vào image. Secret phải inject lúc runtime.
Bước 4: Dùng Docker Compose để quản lý dịch vụ
Production nhỏ và vừa rất hợp với Compose. Nó giúp khai báo app, network, volume, restart policy trong một file.
Ví dụ:
version: "3.9"
services:
app:
build: .
container_name: my-node-app
restart: always
env_file:
- .env
expose:
- "3000"
networks:
- app_net
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
nginx:
image: nginx:stable-alpine
container_name: my-nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app
networks:
- app_net
networks:
app_net:
Điểm đáng chú ý:
– restart: always → app chết sẽ tự lên lại
– expose thay vì ports cho app → chỉ nội bộ Docker thấy
– healthcheck → hỗ trợ giám sát tốt hơn
– Nginx làm public entrypoint
Bước 5: Reverse proxy bằng Nginx
Nginx xử lý: – SSL/TLS – gzip – cache header – giới hạn body size – timeout – proxy tới Node app
Ví dụ config tối thiểu:
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;
}
}
Nếu app dùng WebSocket:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";NodeJS không nên tự ôm SSL ở production VPS nhỏ. Nginx làm tốt hơn, dễ bảo trì hơn.
Bước 6: Biến môi trường, secret, config
Production hay lỗi vì config. Quy tắc chuẩn:
– .env để ngoài image
– không commit secret lên Git
– tách config theo môi trường
– validate env khi app khởi động
Ví dụ:
– PORT
– NODE_ENV
– DATABASE_URL
– JWT_SECRET
– REDIS_URL
App nên fail fast: – thiếu env quan trọng → thoát ngay – không chạy nửa vời rồi lỗi runtime
Nếu dùng framework như NestJS, Express custom config, hãy thêm schema validate cho env.
Bước 7: SSL, domain, bảo mật cơ bản
Production public Internet mà chưa có HTTPS → chưa đạt chuẩn. Tối thiểu cần:
– trỏ domain về IP VPS – cấp chứng chỉ Let’s Encrypt – redirect HTTP → HTTPS – thêm security headers cơ bản
Ngoài ra:
– cập nhật Ubuntu định kỳ
– giới hạn quyền file .env
– không mount toàn bộ source code nếu không cần
– không chạy container privileged
– không để Docker socket lộ ra ngoài
Nếu app upload file: – cân nhắc object storage thay vì lưu trong container – nếu bắt buộc lưu local → mount volume riêng, backup rõ ràng
Bước 8: Quy trình deploy chuẩn, ít downtime
Một flow thực tế:
1. Pull code mới 2. Build image mới 3. Chạy container mới 4. Kiểm tra health 5. Switch traffic 6. Xóa container/image cũ nếu ổn
Với Compose, thường dùng:
docker compose up -d --build
docker compose ps
docker compose logs -f appNếu app migration DB: – chạy migration có kiểm soát – backup trước thay đổi schema lớn – tránh auto migration mù trong mọi lần boot
Rollback: – giữ lại image tag cũ – đổi tag/deploy lại nhanh – DB schema phải tương thích rollback, nếu không rollback app cũng vô ích
Bước 9: Logging, monitoring, backup
Nhiều đội deploy xong coi như xong. Sai. Production thật sự bắt đầu sau deploy.
Cần ít nhất:
– log app ra stdout/stderr
– docker compose logs xem được ngay
– log rotate để tránh đầy disk
– endpoint /health
– cảnh báo khi CPU/RAM/disk tăng bất thường
Backup tối thiểu:
– source code → đã có Git
– .env → backup riêng, mã hóa nếu cần
– DB → backup tự động
– file upload/volume quan trọng → backup định kỳ
VPS nhỏ thường chết không vì code lỗi, mà vì: – disk đầy – RAM cạn – log phình – backup không có – cert hết hạn
Sai lầm phổ biến cần tránh
– Chạy app bằng root → rủi ro bảo mật
– Expose port Node ra public → tăng bề mặt tấn công
– Dùng latest bừa bãi → deploy không tái lập
– Không có healthcheck → khó biết app sống hay chết
– Không pin version Node → build hôm nay khác hôm sau
– Mount cả project vào production container → lẫn lộn dev/prod
– Không backup DB → sự cố nhỏ thành thảm họa
– Deploy trực tiếp trên server không qua image → rollback khó
Kết luận
Deploy NodeJS bằng Docker trên VPS Ubuntu không chỉ là “đóng app vào container”. Quy trình chuẩn cho production là tập hợp nhiều lớp: Dockerfile tối ưu, Compose rõ ràng, Nginx reverse proxy, HTTPS, env an toàn, healthcheck, logging, backup, rollback.
Nếu làm đúng từ đầu, bạn sẽ có một hệ thống dễ mở rộng và dễ vận hành: build một lần, deploy nhất quán, lỗi dễ truy vết, cập nhật ít rủi ro hơn. Với phần lớn dự án vừa và nhỏ, đây là điểm cân bằng rất tốt giữa chi phí VPS và chất lượng vận hành production.
Thực tế nhất: đừng cố làm “quá enterprise” ngay ngày đầu. Hãy bắt đầu với stack tối thiểu nhưng đúng chuẩn: Ubuntu + Docker + Compose + Nginx + SSL + backup. Chừng đó đã đủ để đa số app NodeJS chạy production ổn định, sạch sẽ, chuyên nghiệp.
Bình luận (0)
Chưa có bình luận. Hãy là người đầu tiên!