VPS 📅 25/05/2026 ⏱ 13 phút đọc

DevOps Workflow Trên VPS 2026: Git, Docker, CI/CD Tự Động Deploy Cho Solo Dev & Team Nhỏ

Push code lên GitHub → 2 phút sau code tự động deploy lên VPS. Không cần SSH, không cần copy file, không sợ quên bước. Đó là DevOps workflow — và bài này sẽ hướng dẫn bạn setup từ A-Z trên VPS giá rẻ.

❌ Không DevOps

  • SSH vào VPS mỗi lần update
  • Copy file thủ công
  • Quên restart service → web chết
  • Không rollback được
  • Deploy 2h sáng vì sợ user thấy lỗi

✅ Có DevOps

  • git push → tự deploy
  • Test tự động trước khi deploy
  • Restart service tự động
  • Rollback 1 lệnh
  • Deploy lúc nào cũng được, zero downtime

Các Khái Niệm DevOps Cơ Bản

Khái niệmÝ nghĩaVí dụ
CI (Continuous Integration)Tự động test code mỗi khi pushGitHub Actions chạy npm test mỗi PR
CD (Continuous Delivery)Tự động deploy code đã test lên serverPush main → build Docker → deploy VPS
ContainerĐóng gói app + dependencies vào 1 unitDocker container chạy Node.js + node_modules
PipelineChuỗi các bước CI/CD tự độngTest → Build → Push Image → Deploy
Zero-DowntimeDeploy không làm gián đoạn serviceStart container mới → switch traffic → kill cũ

Setup VPS Ban Đầu

VPS cần cài Docker + Docker Compose. Chạy 3 dòng:

# Cài Docker
curl -fsSL https://get.docker.com | sudo bash
sudo usermod -aG docker $USER

# Cài Docker Compose
sudo apt install -y docker-compose-plugin

# Verify
docker --version          # Docker version 27.x
docker compose version    # Docker Compose v2.x
💡 VPS tối thiểu: Docker nhẹ, chạy được trên VPS 1GB RAM. Nhưng nếu chạy multi-container (app + DB + Redis), nên dùng VPS 2GB RAM trở lên. TrumVPS 4GB chỉ 100K/tháng — quá đủ cho DevOps pipeline.

Docker Hóa Ứng Dụng

Ví dụ ứng dụng Node.js + Express. Tạo Dockerfile:

# Dockerfile
FROM node:22-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Tối ưu build với multi-stage:

# Dockerfile (multi-stage - nhẹ hơn 60%)
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
EXPOSE 3000
CMD ["node", "dist/server.js"]
🎯 Multi-stage benefit: Image cuối cùng chỉ chứa production dependencies + compiled code. Giảm từ 500MB xuống ~150MB. Build nhanh hơn, deploy nhanh hơn.

Docker Compose Cho Multi-Service

App thực tế thường có nhiều service: web + database + redis + nginx. docker-compose.yml:

version: '3.8'
services:
  app:
    build: .
    container_name: myapp
    restart: always
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgres://user:pass@db:5432/mydb
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  db:
    image: postgres:16-alpine
    container_name: myapp_db
    restart: always
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=mydb
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
      interval: 10s
      retries: 5

  redis:
    image: redis:7-alpine
    container_name: myapp_redis
    restart: always
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      retries: 5

  nginx:
    image: nginx:alpine
    container_name: myapp_nginx
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - app

volumes:
  pgdata:
  redis_data:

Deploy 1 lệnh:

docker compose up -d --build

GitHub Actions CI/CD Pipeline

Tạo file .github/workflows/deploy.yml trong repo:

name: Deploy to VPS

on:
  push:
    branches: [main]
  workflow_dispatch:  # Cho phép deploy thủ công

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'
      - run: npm ci
      - run: npm test
      - run: npm run lint

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Deploy via SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          script: |
            cd /opt/myapp
            git pull origin main
            docker compose up -d --build
            docker system prune -f

      - name: Health Check
        run: |
          sleep 10
          curl -f https://yourdomain.com/health || exit 1

      - name: Notify Telegram
        if: always()
        uses: appleboy/telegram-action@master
        with:
          to: ${{ secrets.TELEGRAM_CHAT_ID }}
          token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
          message: |
            🚀 Deploy ${{ job.status }}
            Repo: ${{ github.repository }}
            Branch: ${{ github.ref_name }}
            Commit: ${{ github.sha }}

Setup GitHub Secrets

Vào Repo → Settings → Secrets and variables → Actions, thêm:

SecretGiá trị
VPS_HOSTIP VPS của bạn
VPS_USERdeploy (tạo user riêng, KHÔNG dùng root)
VPS_SSH_KEYPrivate SSH key của user deploy
TELEGRAM_BOT_TOKENToken bot Telegram
TELEGRAM_CHAT_IDChat ID nhận thông báo

Tạo User Deploy Trên VPS (Security Best Practice)

# Trên VPS - tạo user deploy riêng
sudo useradd -m -s /bin/bash deploy
sudo usermod -aG docker deploy
sudo mkdir -p /opt/myapp
sudo chown -R deploy:deploy /opt/myapp

# Tạo SSH key cho GitHub Actions
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/deploy_key
cat ~/.ssh/deploy_key.pub >> /home/deploy/.ssh/authorized_keys
# Copy ~/.ssh/deploy_key → GitHub Secret VPS_SSH_KEY

Rollback & Zero-Downtime Deploy

Khi deploy lỗi, rollback về version cũ trong 1 lệnh:

# Rollback về commit trước đó
git revert HEAD --no-edit
git push origin main
# GitHub Actions tự chạy deploy

Hoặc rollback thủ công ngay trên VPS:

cd /opt/myapp
git log --oneline -5              # Xem 5 commit gần nhất
git reset --hard <COMMIT_HASH>    # Quay về commit cũ
docker compose up -d --build      # Build lại

Zero-downtime với Docker:

# docker-compose.yml thêm:
services:
  app:
    deploy:
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first      # Start container mới trước → kill cũ sau
        failure_action: rollback
🔄 Flow zero-downtime: Docker start container mới → health check OK → route traffic sang container mới → stop container cũ. User không hề thấy gián đoạn.

Monitoring & Health Check

Mọi service nên có health check endpoint:

// server.js - Node.js health endpoint
app.get('/health', async (req, res) => {
  try {
    await db.raw('SELECT 1');          // Check DB
    await redis.ping();                 // Check Redis
    res.json({ status: 'ok', uptime: process.uptime() });
  } catch (e) {
    res.status(503).json({ status: 'error', error: e.message });
  }
});

Kết hợp với Uptime Kuma hoặc Grafana để giám sát 24/7:

Template Cho Các Stack Phổ Biến

Node.js + Express + PostgreSQL

# Dockerfile
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json ./
EXPOSE 3000
CMD ["node", "dist/server.js"]

Python Flask + Redis

# Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"]

Xem thêm: Deploy Python Flask VPS Production

PHP + Nginx + MySQL

# Dockerfile - PHP-FPM
FROM php:8.3-fpm-alpine
RUN docker-php-ext-install pdo pdo_mysql
WORKDIR /var/www/html
COPY . .
# Nginx config trỏ fastcgi_pass tới container php:9000

Static Site (Next.js, Hugo, Astro)

# Build → serve bằng Nginx
FROM node:22-alpine AS builder
WORKDIR /app
COPY . .
RUN npm ci && npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf

Chi Phí DevOps Stack

Thành phầnChi phíGhi chú
VPS (2-4GB RAM)40K - 100K/thángTrumVPS
Docker + Compose$0Open source
GitHub Actions$02000 phút/tháng miễn phí
GitHub Private Repo$0Unlimited
Docker Hub$01 private image miễn phí
Tổng/tháng40K - 100K VNĐFull DevOps pipeline

So với các dịch vụ CI/CD trả phí (CircleCI $30/tháng, GitLab CI $19/tháng), setup self-hosted trên VPS tiết kiệm hàng triệu đồng mỗi năm. Với TrumVPS 4GB RAM chỉ 100K/tháng, bạn có DevOps pipeline chuyên nghiệp với chi phí thấp hơn 1 ly cà phê.

Best Practices Cho Solo Dev

  1. Luôn dùng health check: Docker healthcheck + app /health endpoint. Đừng deploy xong rồi "chắc nó chạy".
  2. Environment variables qua .env file: Đừng hardcode password trong code. Dùng .env + Docker secrets.
  3. Backup database trước deploy: Thêm step backup trong pipeline. pg_dump trước khi migrate.
  4. Feature branch + PR review: Đừng push thẳng main. Mở PR → CI test → merge → CD deploy.
  5. Log tập trung: Dùng docker compose logs -f hoặc Portainer để xem log.
  6. Security: KHÔNG dùng root cho deploy. Tạo user riêng, giới hạn quyền. Firewall chỉ mở port cần thiết.

🚀 Sẵn Sàng Deploy Như Pro?

Cần VPS mạnh để chạy Docker + CI/CD pipeline? TrumVPS từ 40K/tháng, 4GB RAM, SSD NVMe, đủ chạy multi-container. Kích hoạt tự động, hỗ trợ 24/7!

Thuê VPS Ngay - Từ 40K