Khi mỗi lần cập nhật ứng dụng đều phải SSH vào VPS, kéo code, cài dependency và restart service thủ công, rủi ro sai sót tăng dần theo số lần release. GitHub Actions giúp tự động hóa chuỗi build, test và deploy từ repository tới VPS. Bài viết này hướng dẫn một mô hình CI/CD đơn giản nhưng an toàn: dùng SSH key riêng cho deployment, secrets, environment protection, concurrency và script deploy có thể rollback.
Khi dự án còn nhỏ, cách deploy quen thuộc thường là:
- SSH vào VPS;
git pull;- cài dependency;
- build;
- restart service.
Cách này hoạt động, nhưng càng deploy nhiều càng dễ gặp lỗi do thao tác thủ công, deploy chồng nhau hoặc quên một bước. GitHub Actions có thể tự động hóa quy trình này khi code được push lên branch phù hợp.
Mục tiêu của bài viết là xây dựng một pipeline đơn giản, dễ hiểu và có kiểm soát, không cố biến một VPS thành nền tảng orchestration phức tạp.
Kiến trúc CI/CD cơ bản
Một luồng phổ biến:
Developer push code
↓
GitHub Actions
↓
Build / Test
↓
SSH tới VPS
↓
Deploy release mới
↓
Restart service + health check
GitHub Actions chạy workflow dựa trên event như push, pull_request hoặc workflow_dispatch.
Khi nào nên tự động deploy bằng GitHub Actions?
Phù hợp khi:
- ứng dụng chạy trên một hoặc vài VPS;
- team đã lưu code trên GitHub;
- muốn giảm thao tác SSH thủ công;
- cần lịch sử deployment rõ ràng;
- muốn chạy test trước deploy;
- cần tách staging và production.
Nếu bạn đang quản lý nhiều VPS và muốn chuẩn hóa cấu hình toàn hệ thống, Ansible có thể bổ trợ tốt. Xem bài Quản lý nhiều VPS bằng Ansible.
Chuẩn bị user deploy riêng trên VPS
Không nên deploy bằng root nếu không cần.
Tạo user:
sudo adduser deploy
Cấp quyền đúng nhu cầu. Ví dụ nếu app nằm trong:
/var/www/myapp
hãy đảm bảo user deploy có quyền với thư mục đó mà không cần quyền rộng toàn hệ thống.
Nếu service cần restart bằng systemd, có thể cấu hình sudoers rất hẹp thay vì cho quyền sudo toàn phần.
Ví dụ dùng visudo:
sudo visudo
Rule minh họa:
deploy ALL=NOPASSWD:/bin/systemctl restart myapp
Chỉ thêm command thực sự cần thiết.
Tạo SSH key riêng cho CI/CD
Trên máy quản trị:
ssh-keygen -t ed25519 -C "github-actions-deploy" -f github-actions-deploy
Bạn sẽ có:
github-actions-deploy— private key;github-actions-deploy.pub— public key.
Copy public key vào:
/home/deploy/.ssh/authorized_keys
Kiểm tra permission:
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
Không dùng private key cá nhân chính của bạn cho CI/CD.
Lưu secrets trong GitHub
Trong repository, vào phần Actions secrets hoặc Environment secrets và tạo các biến như:
VPS_HOST;VPS_USER;VPS_SSH_KEY;VPS_PORTnếu không dùng 22.
VPS_SSH_KEY chứa private key deployment.
GitHub khuyến nghị dùng secrets cho thông tin nhạy cảm và áp dụng least privilege.
Dùng Environment cho production
GitHub Environments giúp tách:
- staging;
- production;
- secrets theo môi trường;
- deployment rules.
Ví dụ job:
environment: production
Tùy plan và loại repository, bạn có thể dùng required reviewers hoặc branch restrictions.
Điểm quan trọng là production secrets chỉ được dùng bởi job nhắm đúng environment.
Workflow đầu tiên
Tạo file:
.github/workflows/deploy.yml
Ví dụ cơ bản:
name: Deploy to VPS
on:
push:
branches:
- main
workflow_dispatch:
concurrency:
group: production-deploy
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run tests
run: |
echo "Run your test command here"
- name: Configure SSH
env:
SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
VPS_HOST: ${{ secrets.VPS_HOST }}
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
printf '%s\n' "$SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Deploy
env:
VPS_HOST: ${{ secrets.VPS_HOST }}
VPS_USER: ${{ secrets.VPS_USER }}
run: |
ssh "$VPS_USER@$VPS_HOST" '/opt/myapp/deploy.sh'
Vì sao cần known_hosts?
SSH cần xác minh host key để chống kết nối nhầm hoặc man-in-the-middle.
ssh-keyscan tiện cho automation nhưng nếu security yêu cầu cao, nên xác minh fingerprint trước và lưu giá trị tin cậy thay vì chấp nhận bất kỳ host key nào tại runtime.
Một cách chặt chẽ hơn là lưu fingerprint/known_hosts đã xác minh thành secret hoặc repository variable phù hợp.
Tách logic deploy khỏi workflow
Thay vì nhét toàn bộ command vào YAML, nên có script trên VPS:
#!/usr/bin/env bash
set -euo pipefail
APP_DIR="/var/www/myapp"
cd "$APP_DIR"
git fetch origin main
git reset --hard origin/main
npm ci
npm run build
sudo systemctl restart myapp
Lưu tại:
/opt/myapp/deploy.sh
Cấp quyền:
sudo chmod 750 /opt/myapp/deploy.sh
sudo chown deploy:deploy /opt/myapp/deploy.sh
set -euo pipefail giúp script dừng khi gặp lỗi thay vì tiếp tục trong trạng thái không rõ ràng.
Nhưng git reset --hard có nguy hiểm không?
Có, nếu bạn lưu file chỉnh tay trong working tree.
Với production, repository nên được xem là immutable source. File config/secrets nên nằm ngoài Git hoặc được inject qua environment/config management.
Nếu bạn có dữ liệu người dùng trong thư mục project, tuyệt đối không để lệnh deploy xóa nhầm.
Mô hình release directory an toàn hơn
Thay vì deploy trực tiếp vào một thư mục, có thể dùng:
/var/www/myapp/
├── releases/
│ ├── 20260928-090100/
│ ├── 20260928-103000/
│ └── 20260928-121500/
├── current -> releases/20260928-121500
└── shared/
Mỗi lần deploy:
- tạo release mới;
- build trong release;
- link config/shared data;
- health check;
- đổi symlink
current; - reload service.
Nếu lỗi, rollback bằng cách đổi symlink về release trước.
Health check sau deploy
Workflow không nên coi deploy thành công chỉ vì SSH command trả exit code 0.
Ví dụ:
curl --fail --silent --show-error https://example.com/health
Nếu API trả non-2xx, command sẽ fail.
Bạn cũng có thể kiểm tra local service:
curl --fail http://127.0.0.1:3000/health
Deploy Docker Compose
Nếu app dùng Docker Compose:
#!/usr/bin/env bash
set -euo pipefail
cd /opt/myapp
git fetch origin main
git reset --hard origin/main
docker compose pull
docker compose up -d --build
docker compose ps
Không chạy docker compose down nếu không cần, vì có thể tạo downtime không cần thiết.
Nếu bạn mới dùng Docker, xem Cài Docker và Docker Compose trên VPS Ubuntu.
Không đưa secrets vào repository
Tránh commit:
.env
private.key
production.json
Dùng:
- GitHub secrets;
- Environment secrets;
- secret manager;
- file
.envđược quản lý riêng trên VPS.
Không echo secret ra log để debug.
Giới hạn quyền của workflow
GitHub khuyến nghị cấp tối thiểu quyền cho GITHUB_TOKEN.
Ví dụ:
permissions:
contents: read
Nếu workflow không cần ghi repository, đừng cấp write permission.
Tránh deploy chồng nhau bằng concurrency
Nếu hai commit được push liên tiếp, hai workflow có thể cùng deploy.
Dùng:
concurrency:
group: production-deploy
cancel-in-progress: false
Giúp tại một thời điểm chỉ có một deployment production đang chạy.
Tách staging và production
Một chiến lược tốt:
- branch
develop→ staging; - branch
main→ production; - production dùng environment riêng;
- test phải pass trước deploy;
- manual approval nếu cần.
Đừng dùng chung một private key và một set secrets cho mọi môi trường nếu không cần.
Rollback như thế nào?
Một pipeline không có rollback chưa hoàn chỉnh.
Có thể rollback bằng:
- release directory + symlink;
- image Docker version cũ;
- Git tag/release đã xác nhận;
- restore database nếu migration không tương thích.
Database migration là phần khó nhất vì rollback code chưa chắc rollback schema được.
Với migration quan trọng, nên:
- backup;
- dùng migration backward-compatible;
- deploy app;
- kiểm tra;
- chỉ xóa column/schema cũ ở release sau.
Sai lầm thường gặp
Dùng root SSH key trong GitHub Secrets
Blast radius quá lớn. Dùng deploy user riêng và quyền tối thiểu.
Tắt host key checking
StrictHostKeyChecking=no tiện nhưng làm yếu xác minh SSH. Nên quản lý known_hosts đúng cách.
Deploy trực tiếp khi test chưa chạy
Pipeline nên fail sớm trước khi chạm production.
Không có concurrency
Hai deployment song song có thể ghi đè file hoặc restart service chồng nhau.
Không có health check
Workflow xanh không có nghĩa ứng dụng thật sự phục vụ được traffic.
Checklist CI/CD an toàn
- User deploy riêng.
- SSH key riêng cho automation.
- Secrets không commit vào Git.
-
GITHUB_TOKENleast privilege. - Production environment riêng.
- Test trước deploy.
- Có concurrency.
- Host key được xác minh.
- Deploy script dùng
set -euo pipefail. - Có health check.
- Có rollback plan.
- Có backup trước migration lớn.
FAQ
GitHub Actions có deploy được lên VPS không?
Có. Một mô hình phổ biến là workflow chạy test, sau đó SSH tới VPS bằng deploy key và gọi script triển khai.
Có nên lưu SSH private key trong GitHub Secrets?
Có thể, nếu key đó dành riêng cho deployment, giới hạn quyền và được xoay vòng định kỳ. Không dùng private key cá nhân chính.
Nên deploy bằng git pull hay build artifact?
Project nhỏ có thể dùng Git trên server. Hệ thống nghiêm ngặt hơn thường build artifact/image trước rồi deploy immutable artifact để dễ kiểm soát và rollback.
Có cần manual approval trước production không?
Tùy mức rủi ro. Với hệ thống quan trọng, environment approval giúp giảm deploy nhầm.
CI/CD có thay Ansible không?
Không. GitHub Actions điều phối pipeline, còn Ansible phù hợp quản lý cấu hình nhiều máy. Hai công cụ có thể phối hợp.
Kết luận
GitHub Actions giúp biến quy trình deploy VPS từ thao tác thủ công thành pipeline có thể lặp lại, audit và kiểm soát.
Hãy bắt đầu nhỏ: test → SSH bằng deploy user → chạy script → health check. Sau đó mới mở rộng sang staging, environment protection, immutable artifact và rollback tự động.
Nếu bạn đang cần VPS để chạy môi trường staging hoặc production cho ứng dụng web, có thể tham khảo các dịch vụ VPS tại HUONG CLOUD và lựa chọn tài nguyên theo workload thực tế.
Nguồn tham khảo
- GitHub Docs — Deploying with GitHub Actions: https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/control-deployments
- GitHub Docs — Deployments and environments: https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments
- GitHub Docs — Secure use reference: https://docs.github.com/en/actions/reference/security/secure-use
Kết luận
Nếu chưa chắc cấu hình hoặc loại dịch vụ nào phù hợp, hãy gửi nhu cầu thực tế để đội ngũ hỗ trợ tư vấn trước khi mua.
Chưa biết nên chọn gói nào?
Xem bảng giá VPS hoặc liên hệ đội ngũ hỗ trợ để chọn cấu hình phù hợp.
Xem bảng giá VPS →