Deploy nhiều app NodeJS bằng Docker trên 1 VPS an toàn, dễ quản lý

08/05/2026 · P T P · Chung

Deploy nhiều app NodeJS trên cùng một VPS bằng Docker: an toàn, gọn, dễ vận hành

Chạy nhiều app NodeJS trên một VPS nghe đơn giản: cài Node, clone repo, pm2 start, xong. Nhưng rất nhanh sẽ gặp hỗn loạn: port đụng nhau, log khó đọc, SSL rối, update dễ downtime, một app lỗi kéo theo cả máy nghẹt tài nguyên. Quy mô nhỏ vẫn chịu được. Quy mô vừa → bắt đầu đau.

Giải pháp thực dụng nhất cho đa số team nhỏ, startup, freelancer: Docker + reverse proxy + tách mạng + quy ước deploy rõ ràng. Mục tiêu không phải “enterprise”, mà là: an toàn hơn, deploy nhanh hơn, rollback dễ hơn, quản lý đỡ mệt hơn.

Bài này đi thẳng vào mô hình triển khai nhiều app NodeJS trên cùng một VPS theo hướng ổn định, bảo mật cơ bản tốt, dễ bảo trì.

Vì sao nên dùng Docker thay vì chạy thẳng Node trên VPS?

Chạy thẳng nhiều app NodeJS trên OS host thường dẫn tới:

Xung đột môi trường: app A cần Node 18, app B cần Node 20.
Port lộn xộn: app nào cũng muốn 3000, 4000.
Deploy khó chuẩn hóa: mỗi app một script.
Rollback kém: update lỗi → sửa tay.
Khó cô lập: app ăn RAM/CPU quá mức → ảnh hưởng app khác.

Docker giải quyết khá gọn:

Mỗi app = một container → cô lập runtime.
Image bất biến → build 1 lần, chạy ổn định.
Port nội bộ riêng → reverse proxy route domain đúng app.
Dễ CI/CDdocker compose pull && up -d.
Rollback nhanh → quay lại image cũ.

Docker không tự làm hệ thống “an toàn tuyệt đối”. Nhưng nó tạo nền tốt để bạn kiểm soát tốt hơn.

Kiến trúc nên dùng: 1 reverse proxy, nhiều app container

Mô hình khuyến nghị:

1 VPS
1 reverse proxy container: Nginx, Traefik, hoặc Caddy
N app NodeJS, mỗi app một container
Database:
– tốt nhất: tách VPS/managed DB
– chấp nhận được: DB container riêng, không public port
Docker network riêng
SSL terminate tại reverse proxy

Luồng chuẩn:

– User vào app1.example.com
– Reverse proxy nhận request
– Proxy route tới container app1
– App xử lý
– Response trả về qua proxy

Lợi ích lớn:

– Chỉ mở public 80/443
– App NodeJS không cần public port ra Internet
– SSL tập trung một chỗ
– Domain/subdomain mapping rõ ràng

Cấu trúc thư mục nên gọn, chuẩn ngay từ đầu

Trên VPS, nên thống nhất kiểu tổ chức:

/opt/apps/
  reverse-proxy/
  app-a/
  app-b/
  app-c/

Mỗi app nên có:

Dockerfile
docker-compose.yml hoặc nằm trong compose tổng
.env
– thư mục volume nếu cần log/upload

Ví dụ:

/opt/apps/app-a/
  Dockerfile
  docker-compose.yml
  .env

Đừng để mỗi app một kiểu. Kỷ luật thư mục → vận hành nhàn hơn rất nhiều.

Dockerfile cho NodeJS: nhỏ, sạch, production-first

Một lỗi phổ biến: dùng cùng image dev để chạy production. Kết quả: image nặng, nhiều package thừa, build chậm, bề mặt tấn công rộng hơn.

Nên dùng multi-stage build:

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/index.js"]

Điểm quan trọng:

node:alpine → image nhẹ
npm ci → reproducible
--omit=dev → production gọn hơn
USER node → không chạy bằng root
– Chỉ copy thứ cần thiết

Thêm .dockerignore:

node_modules
npm-debug.log
.git
.env
dist

.env không nên bake vào image.

Docker Compose: cách đơn giản nhất để quản lý nhiều app

Với 1 VPS, docker compose đủ tốt cho đa số nhu cầu. Chưa cần Kubernetes.

Ví dụ app:

services:
  app-a:
    build: .
    container_name: app-a
    restart: unless-stopped
    env_file:
      - .env
    networks:
      - web
    expose:
      - "3000"
    mem_limit: 512m
    cpus: 0.50

networks: web: external: true

Ý chính:

restart: unless-stopped → tự khởi động lại
expose thay vì ports → chỉ mở nội bộ Docker
giới hạn RAM/CPU → app lỗi không kéo sập VPS
– dùng external network chung cho proxy + app

Tạo network 1 lần:

docker network create web

Reverse proxy: mảnh ghép quan trọng nhất

Nếu muốn dễ nhất, Caddy rất đáng dùng vì tự cấp SSL. Nếu muốn phổ biến hơn, chọn Nginx + Certbot. Nếu muốn tự động routing mạnh, chọn Traefik.

Với người mới, Caddy thường ít đau đầu nhất.

Ví dụ Caddyfile:

app1.example.com {
    reverse_proxy app-a:3000
}

app2.example.com { reverse_proxy app-b:3000 }

Compose cho Caddy:

services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - web

volumes: caddy_data: caddy_config:

networks: web: external: true

Kết quả:

– SSL tự cấp/tự renew
– app không cần publish port
– thêm app mới → thêm 1 block domain

Bảo mật: tối thiểu phải làm đúng

Đây là phần nhiều người bỏ qua nhất.

1. Chỉ mở port cần thiết

Firewall VPS:

– mở 22, 80, 443
– đóng mọi port khác
– nếu được, giới hạn SSH theo IP

Ubuntu dùng ufw:

ufw allow OpenSSH
ufw allow 80
ufw allow 443
ufw enable

2. Tắt SSH password, dùng key

– dùng SSH key
– tắt login bằng password
– nếu có thể, tắt root login

SSH hở password → bot quét liên tục.

3. Không public database

Nếu chạy PostgreSQL/MySQL trên cùng VPS:

không map 5432:5432 hoặc 3306:3306 ra ngoài
– chỉ cho app truy cập qua Docker network

DB public port → rủi ro tăng mạnh.

4. Secrets không hard-code

Không commit:

.env
– API key
– DB password
– JWT secret

Tối thiểu:

– lưu trong .env trên VPS
– phân quyền file chặt
– tách env theo app

5. Chạy container non-root

Trong app container:

– dùng USER node
– tránh mount quyền ghi tràn lan
– nếu không cần, mount filesystem read-only

6. Giới hạn tài nguyên

Một app memory leak → cả VPS swap, treo. Fix:

mem_limit
cpus
– log rotation

Logging, monitoring, backup: đừng đợi có sự cố mới làm

Nhiều app trên 1 VPS mà không có quan sát → mù.

Logging

Tối thiểu:

– app log ra stdout/stderr
– xem bằng:

docker logs app-a --tail 100 -f

Bật log rotation ở Docker daemon hoặc dùng driver phù hợp. Nếu không, log phình đầy disk.

Monitoring

Ít nhất theo dõi:

– CPU
– RAM
– disk
– restart count container
– uptime

Tool nhẹ:

htop
docker stats
– Uptime Kuma
– Netdata

Backup

Cần backup:

– database
– file upload
– file cấu hình như Caddyfile, compose, .env

Nguyên tắc:

– backup tự động hàng ngày
– lưu ngoài VPS
– test restore định kỳ

Backup không test restore → gần như chưa có backup.

Quy trình deploy nên đơn giản, lặp lại được

Một flow thực dụng:

1. Build image từ CI hoặc ngay trên VPS
2. Pull code/image mới
3. Chạy:

docker compose up -d --build

4. Check health/log
5. Nếu lỗi → rollback image cũ

Tốt hơn nữa:

– gắn tag image theo version/git commit
– không dùng mãi latest
– thêm healthcheck để proxy không route vào app chưa sẵn sàng

Ví dụ:

healthcheck:
  test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
  interval: 30s
  timeout: 5s
  retries: 3

App có endpoint /health → deploy an toàn hơn nhiều.

Khi nào mô hình này đủ, khi nào nên nâng cấp?

Mô hình 1 VPS + Docker phù hợp nếu:

– 2-10 app nhỏ/vừa
– traffic chưa quá lớn
– team ít người
– cần tối ưu chi phí
– muốn deploy nhanh, quản lý đơn giản

Nên nghĩ tới nhiều VPS, orchestrator, hoặc managed platform khi:

– cần high availability
– cần auto-scaling
– traffic tăng mạnh
– yêu cầu zero-downtime chặt
– nhiều người cùng vận hành

Đừng “Kubernetes quá sớm”. Nhiều trường hợp chỉ làm hệ thống phức tạp hơn.

Kết luận

Deploy nhiều app NodeJS trên cùng một VPS không khó. Khó ở chỗ giữ nó gọn, an toàn, dễ sống cùng lâu dài. Công thức thực tế nhất thường là:

Docker để cô lập app
Docker Compose để quản lý
Reverse proxy để route domain + SSL
Firewall + SSH key + DB không public để giảm rủi ro
Giới hạn tài nguyên + backup + monitoring để tránh sự cố lan rộng

Nếu làm đúng từ đầu, bạn sẽ có một hệ thống:

– deploy nhanh
– thêm app dễ
– rollback gọn
– ít port hở
– dễ debug hơn nhiều so với chạy tay từng app

Tóm ngắn: 1 VPS vẫn đủ mạnh và chuyên nghiệp, miễn là bạn triển khai có quy ước, có cô lập, có bảo mật tối thiểu. Không cần quá nhiều công cụ. Cần đúng công cụ, đúng cấu trúc, đúng kỷ luật vận hành.

#bang #deploy #docker #nhieu #nodejs
Chia sẻ:
← Trước
Deploy NodeJS bằng Docker trên VPS Ubuntu chuẩn production A-Z

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!