Tự động deploy NodeJS bằng Docker trên VPS với GitHub Actions: nhanh, sạch, ít lỗi
Deploy thủ công thường bắt đầu rất “ổn”: SSH vào VPS, git pull, npm install, restart app. Nhưng chỉ vài lần là lộ vấn đề: quên pull đúng branch, quên migrate, lệch môi trường, downtime lúc restart, “máy em chạy được”. Quy trình nhỏ → rủi ro lớn.
Giải pháp gọn, thực dụng: đóng gói app NodeJS bằng Docker, đẩy code lên GitHub, dùng GitHub Actions để tự động build và deploy lên VPS. Kết quả: mỗi lần push → pipeline chạy → VPS cập nhật đồng nhất, dễ rollback, dễ mở rộng.
Bài này đi theo hướng thực chiến: ít lý thuyết, tập trung thứ bạn cần để chạy được.
Vì sao nên kết hợp Docker + VPS + GitHub Actions?
Docker → môi trường đồng nhất
NodeJS rất hay dính lỗi kiểu:
– local Node 20, server Node 18 – local có package, server thiếu – app phụ thuộc binary/native module khác OS
Docker giải quyết bằng cách đóng gói app + runtime + dependency thành image. Local chạy sao, server chạy gần như vậy.
VPS → rẻ, chủ động, đủ mạnh cho nhiều dự án
Với app vừa và nhỏ, VPS thường là lựa chọn cân bằng:
– rẻ hơn PaaS – toàn quyền cài Nginx, Docker, firewall – dễ host nhiều service trên cùng máy
Đổi lại: bạn phải tự lo deploy, bảo mật, backup. GitHub Actions giúp giảm mạnh phần “tay chân”.
GitHub Actions → CI/CD ngay trong repo
Không cần dựng Jenkins hay GitLab Runner riêng. Chỉ cần file workflow là có thể:
– chạy test – build image – SSH vào VPS – pull code/image – restart container
Push code → deploy tự động. Nhất quán, ít quên bước.Kiến trúc triển khai đề xuất
Một flow phổ biến, dễ áp dụng:
1. Dev push code lên branch main
2. GitHub Actions được kích hoạt
3. Workflow SSH vào VPS
4. VPS pull code mới hoặc nhận lệnh cập nhật
5. Docker Compose rebuild/restart container
6. App NodeJS chạy phiên bản mới
Có 2 hướng chính:
– Hướng A: build ngay trên VPS – đơn giản – không cần registry – hợp dự án nhỏ
– Hướng B: build image trên GitHub Actions, push lên registry rồi VPS pull – sạch hơn – deploy nhanh hơn – hợp team/prod
Trong bài này, ưu tiên Hướng A vì dễ bắt đầu nhất.
Chuẩn bị trên VPS
Trên VPS Ubuntu, cài Docker và Docker Compose plugin:
sudo apt update
sudo apt install -y docker.io docker-compose-plugin git
sudo systemctl enable docker
sudo systemctl start dockerCho phép user hiện tại dùng Docker không cần sudo:
sudo usermod -aG docker $USERĐăng xuất rồi đăng nhập lại.
Tiếp theo, clone project về thư mục deploy, ví dụ:
mkdir -p /var/www/myapp
cd /var/www/myapp
git clone https://github.com/yourname/your-repo.git .Nếu app dùng file môi trường, tạo .env ngay trên VPS:
PORT=3000
NODE_ENV=production
DATABASE_URL=...
JWT_SECRET=....env lên GitHub.
Dockerize ứng dụng NodeJS
Dockerfile cơ bản
Tạo file Dockerfile:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Ý nghĩa:
– node:20-alpine → image nhẹ
– npm ci → cài dependency ổn định theo lockfile
– --omit=dev → production gọn hơn
Nếu app cần build, ví dụ TypeScript/NestJS, có thể dùng multi-stage build. Nhưng với nhiều app Express/Fastify đơn giản, file trên là đủ.
docker-compose.yml
Tạo docker-compose.yml:
version: "3.9"
services:
app:
build: .
container_name: myapp
restart: always
ports:
- "3000:3000"
env_file:
- .env
Chạy thử trên VPS:
docker compose up -d --buildKiểm tra:
docker ps
docker logs -f myappNếu app chạy ổn tại đây, phần khó nhất đã qua.
Cấu hình SSH an toàn cho GitHub Actions
Để GitHub Actions SSH vào VPS, nên dùng SSH key, không dùng password.
Trên máy local, tạo key:
ssh-keygen -t ed25519 -C "github-actions-deploy"Copy public key lên VPS:
ssh-copy-id user@your_vps_ipHoặc tự thêm nội dung file .pub vào:
~/.ssh/authorized_keysSau đó, vào GitHub repo → Settings → Secrets and variables → Actions, thêm các secret:
– VPS_HOST → IP/domain VPS
– VPS_USER → user SSH
– VPS_SSH_KEY → nội dung private key
– VPS_PORT → thường là 22
Nếu dùng .env sinh động từ CI, có thể thêm secret DB/auth tương ứng. Nhưng dễ nhất vẫn là giữ .env trên VPS.
Tạo workflow GitHub Actions để deploy tự động
Tạo file:
.github/workflows/deploy.ymlNội dung:
name: Deploy NodeJS to VPS
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy via SSH
uses: appleboy/[email protected]
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ secrets.VPS_PORT }}
script: |
cd /var/www/myapp
git pull origin main
docker compose down
docker compose up -d --build
docker image prune -f
Flow này rất rõ:
– push main → trigger
– GitHub Actions SSH vào VPS
– git pull → lấy code mới
– docker compose up -d --build → rebuild + chạy lại
Nếu muốn giảm downtime, có thể bỏ docker compose down và dùng:
docker compose up -d --buildCompose sẽ recreate service khi cần. Thực tế ổn hơn cho app nhỏ.
Reverse proxy và domain: nên có
Chạy trực tiếp cổng 3000 được, nhưng production nên đặt Nginx phía trước:
– map domain – SSL với Let’s Encrypt – gzip/cache header – ẩn app port nội bộ
Ví dụ Nginx reverse proxy tới localhost:3000. Khi đó user chỉ truy cập https://yourdomain.com, còn container vẫn giữ cổng riêng.
Nếu bạn đã có Nginx ngoài Docker, mô hình sẽ gọn: Nginx host → Node container.
Một số cải tiến đáng giá
Thêm bước test trước deploy
Deploy mọi commit lên production mà không test → rủi ro. Có thể thêm:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm testtest fail → không deploy.
Healthcheck
App khởi động lỗi mà container vẫn restart liên tục → khó nhận biết. Nên có route /health và healthcheck trong Docker.
Tách môi trường staging/prod
– main → production
– develop → staging
Mỗi branch → 1 workflow hoặc 1 VPS khác. Team làm việc an toàn hơn.
Rollback
Cách đơn giản nhất khi deploy kiểu pull code trên VPS:
git log --oneline
git checkout <commit>
docker compose up -d --buildKhông “đẹp” bằng rollback theo image tag, nhưng đủ cứu production lúc gấp.
Lỗi thường gặp
Permission denied (publickey)
Nguyên nhân:
– sai private key trong GitHub Secret
– user SSH sai
– public key chưa nằm trong authorized_keys
Fix: test SSH từ local trước, rồi copy đúng private key vào secret.
docker: permission denied
User deploy chưa thuộc group docker.
Fix:
sudo usermod -aG docker $USERĐăng nhập lại.
Container build được nhưng app crash
Thường do:
– thiếu biến môi trường
– npm start sai command
– app bind sai host/port
Với NodeJS, nên bảo đảm app listen trên 0.0.0.0, không phải chỉ localhost.
Ví dụ:
app.listen(process.env.PORT || 3000, '0.0.0.0');git pull bị conflict trên VPS
Nguyên nhân: bạn sửa file trực tiếp trên server.
Fix: tránh sửa code trên VPS. Server chỉ nên là nơi chạy app. Config tách qua .env, Nginx, secret.
Kết luận
Nếu bạn muốn một quy trình deploy gọn, rẻ, dễ kiểm soát, bộ ba NodeJS + Docker + VPS + GitHub Actions là lựa chọn rất mạnh. Nó không quá phức tạp như Kubernetes, nhưng chuyên nghiệp hơn hẳn việc SSH tay mỗi lần release.
Công thức cốt lõi:
– Docker → đồng nhất môi trường – VPS → chủ động hạ tầng – GitHub Actions → tự động hóa deploy – SSH + Compose → triển khai nhanh, dễ hiểu
Điểm quan trọng nhất không phải “xịn” đến đâu, mà là ổn định và lặp lại được. Một pipeline đơn giản, chạy chắc, luôn tốt hơn quy trình cầu kỳ nhưng khó bảo trì.
Nếu bạn mới bắt đầu, hãy triển khai phiên bản tối thiểu:
1. Dockerize app 2. Chạy ổn trên VPS bằng Compose 3. Tạo SSH key 4. Viết workflow GitHub Actions 5. Push một commit thử nghiệm
Chỉ cần vậy, bạn đã có CI/CD thực dụng cho dự án NodeJS. Từ đó, mới nâng cấp dần: test, healthcheck, rollback, registry, zero-downtime. Làm từng bước → ít lỗi, dễ vận hành, dễ ngủ ngon hơn sau mỗi lần deploy.
Bình luận (0)
Chưa có bình luận. Hãy là người đầu tiên!