Deploy NodeJS API lên VPS với Docker và Nginx Chuẩn Nhanh Gọn

06/05/2026 · P T P · Chung

Deploy NodeJS API lên VPS bằng Docker + Nginx reverse proxy: gọn, sạch, dễ bảo trì

Deploy NodeJS API lên VPS nghe đơn giản: cài Node, git pull, chạy pm2, mở port. Làm được, nhưng dễ vỡ: lệch môi trường, restart lỗi, SSL rối, scale khó, rollback mệt. Cách ổn hơn: đóng gói app bằng Docker, để Nginx làm reverse proxy, nhận HTTPS, chuyển tiếp req vào container API.

Mô hình này phổ biến vì rõ trách nhiệm:

Docker → chuẩn hóa môi trường chạy
NodeJS container → chỉ lo business logic
Nginx → public entrypoint, reverse proxy, SSL, gzip, rate limit cơ bản
VPS → host duy nhất, chi phí thấp, đủ cho nhiều side project/sản phẩm nhỏ

Bài này hướng dẫn triển khai thực chiến: từ chuẩn bị VPS, viết Dockerfile, docker-compose.yml, cấu hình Nginx, đến vận hành/log/debug.

Kiến trúc mục tiêu

Luồng req:

– Client → Nginx :80/:443
– Nginx → proxy → NodeJS API container :3000
– Docker network nội bộ → container nói chuyện với nhau
– Chỉ Nginx expose ra Internet

Ưu điểm:

Tách biệt tầng web/app
Ẩn port app nội bộ
Deploy/redeploy nhanh
Rollback dễ
Dễ thêm SSL bằng Let’s Encrypt

1. Chuẩn bị VPS

Tối thiểu:

– Ubuntu 22.04 hoặc 20.04
– RAM 1GB+ nếu API nhỏ
– Domain trỏ A record về IP VPS
– SSH access

Cập nhật hệ thống:

sudo apt update && sudo apt upgrade -y

Cài Docker + Compose plugin:

sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Cho user hiện tại dùng Docker không cần sudo:

sudo usermod -aG docker $USER
newgrp docker

Mở firewall:

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enable

2. Cấu trúc project NodeJS phù hợp để container hóa

Ví dụ:

my-api/
├── src/
├── package.json
├── package-lock.json
├── .env
├── Dockerfile
├── .dockerignore
└── docker-compose.yml

Điểm cần nhớ:

– App phải listen 0.0.0.0, không phải localhost
– Port nội bộ ví dụ 3000
– Secret/env không hard-code

Ví dụ Express:

const express = require('express');
const app = express();

app.get('/health', (req, res) => res.json({ ok: true }));

const PORT = process.env.PORT || 3000; app.listen(PORT, '0.0.0.0', () => { console.log(API running on ${PORT}); });

3. Viết Dockerfile tối ưu cho NodeJS API

Dockerfile:

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./ RUN npm ci --omit=dev

COPY . .

ENV NODE_ENV=production EXPOSE 3000

CMD ["node", "src/index.js"]

Giải thích nhanh:

node:20-alpine → image nhẹ
npm ci → install ổn định theo lockfile
--omit=dev → giảm dung lượng production
EXPOSE 3000 → tài liệu hóa port app

Thêm .dockerignore để build sạch hơn:

node_modules
npm-debug.log
.git
.env
Dockerfile
docker-compose.yml

node_modules local khác môi trường Linux → bỏ, để container tự cài.

4. Dùng Docker Compose để quản lý container

docker-compose.yml:

version: "3.9"

services: api: build: . container_name: my-api restart: always env_file: - .env expose: - "3000" networks: - appnet

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 - ./certbot/www:/var/www/certbot - ./certbot/conf:/etc/letsencrypt depends_on: - api networks: - appnet

networks: appnet:

Lưu ý:

api dùng expose → chỉ mở cho network nội bộ
nginx dùng ports → public ra ngoài
restart: always → VPS reboot xong tự lên lại

5. Cấu hình Nginx reverse proxy

Tạo file nginx/default.conf:

server {
    listen 80;
    server_name api.yourdomain.com;

location /.well-known/acme-challenge/ { root /var/www/certbot; }

location / { proxy_pass http://api: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; } }

Ý nghĩa:

proxy_pass http://api:3000; → Nginx gọi container theo service name api
– Header X-Forwarded-* → app biết IP thật, protocol thật
– ACME path → phục vụ xác thực domain khi cấp SSL

Khởi động lần đầu:

docker compose up -d --build

Test:

curl http://api.yourdomain.com/health

Nếu ra JSON → reverse proxy ok.

6. Cấp HTTPS bằng Let’s Encrypt

Có nhiều cách. Thực dụng nhất trên VPS nhỏ: dùng Certbot container hoặc cài certbot host. Ví dụ cài trên host:

sudo apt install -y certbot

Dừng Nginx container tạm nếu cần chiếm port 80. Hoặc giữ cấu hình ACME qua webroot. Tạo cert:

sudo certbot certonly --webroot -w ./certbot/www -d api.yourdomain.com

Sau khi có cert, cập nhật nginx/default.conf:

server {
    listen 80;
    server_name api.yourdomain.com;

location /.well-known/acme-challenge/ { root /var/www/certbot; }

location / { return 301 https://$host$request_uri; } }

server { listen 443 ssl http2; server_name api.yourdomain.com;

ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;

location / { proxy_pass http://api: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; } }

Reload:

docker compose restart nginx

Kết quả:

– HTTP → tự redirect HTTPS
– SSL terminate tại Nginx
– API container không cần xử lý chứng chỉ

7. Quy trình deploy thực tế

Một flow đơn giản:

1. SSH vào VPS
2. Pull code mới
3. Rebuild image
4. Restart container

Lệnh:

git pull origin main
docker compose up -d --build

Nếu thay đổi chỉ ở code app → Compose rebuild service api. Nếu thay đổi config Nginx → container Nginx cũng reload lại.

Muốn dọn image cũ:

docker image prune -f

8. Log, debug, health check

Xem log app:

docker compose logs -f api

Xem log Nginx:

docker compose logs -f nginx

Kiểm tra container:

docker ps

Vào shell trong container:

docker exec -it my-api sh

Một số lỗi phổ biến:

502 Bad Gateway → app chết, sai port, Nginx không gọi được api:3000
Connection refused → app listen localhost thay vì 0.0.0.0
SSL không lên → DNS chưa trỏ đúng, port 80/443 bị chặn
Env không ăn → quên env_file, quên restart container

Nên có endpoint /health để monitor nhanh.

9. Best practices để chạy lâu dài

Vài khuyến nghị đáng tiền:

Không expose port API ra ngoài nếu đã có Nginx
Dùng .env riêng production
Set NODE_ENV=production
Giới hạn log nếu app ghi quá nhiều
Backup database riêng nếu DB cùng VPS
Pin version image nếu cần ổn định cao
Theo dõi tài nguyên bằng htop, docker stats

Nếu traffic tăng:

– Nginx vẫn giữ vai trò proxy
– Có thể scale nhiều API container
– Sau đó cân nhắc Kubernetes/Swarm hoặc tách DB/cache sang managed service

Kết luận

Deploy NodeJS API lên VPS bằng Docker + Nginx reverse proxy là điểm cân bằng rất tốt giữa đơn giảnchuẩn production cơ bản. Docker giải quyết bài toán “máy em chạy được”, Nginx xử lý public traffic và HTTPS, còn VPS giúp tiết kiệm chi phí cho giai đoạn đầu.

Nếu bạn đang chạy NodeJS bằng cách SSH vào server rồi node app.js, đây là lúc nâng cấp. Chỉ cần thiết lập một lần, các lần deploy sau gần như còn đúng 2 lệnh: git pulldocker compose up -d --build.

Ít rủi ro hơn. Dễ debug hơn. Dễ mở rộng hơn. Với đa số API nhỏ và vừa, vậy là quá đủ để đi đường dài.

#chuan #deploy #docker #nginx #nodejs
Chia sẻ:
← Trước
Checklist Deploy NodeJS Bằng Docker Trên VPS Nhanh Gọn Ít Lỗi 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!