Deploy NodeJS bằng Docker trên VPS Ubuntu chuẩn production A-Z

07/05/2026 · P T P · Chung

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 enable

Nế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 app

Nế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.

#bang #deploy #docker #nodejs #tren
Chia sẻ:
← Trước
Cách Cấu Hình SSL Miễn Phí Cho NodeJS Docker Trên VPS Chuẩn Nhất

Bài viết tương tự

Bình luận

Chưa có bình luận. Hãy là người đầu tiên!