目标

  • git push 到 main 分支 → 自动 ssh 到 VPS → 拉新代码 → 重启服务
  • 中间的"测试 → 构建 → 部署"全自动,不要再手动登录服务器了

最小流水线

.github/workflows/deploy.yml:

name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup SSH
        uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: ${{ secrets.DEPLOY_KEY }}

      - name: Deploy
        run: |
          ssh -o StrictHostKeyChecking=no wadely@${{ secrets.SERVER_IP }} '
            cd /home/wadely/myapp &&
            git pull &&
            docker compose up -d --build
          '

配置秘钥

  1. 服务器上为部署专门生一对 key:
    ssh-keygen -t ed25519 -f ~/.ssh/deploy_key
    cat ~/.ssh/deploy_key.pub >> ~/.ssh/authorized_keys
    
  2. 私钥 (deploy_key) 加到 GitHub 仓库 Secrets:Settings → Secrets and variables → Actions → New secret,名字 DEPLOY_KEY,值是私钥文件全部内容
  3. 同样加 SERVER_IP

完整版:测试 + 构建 + 部署

name: CI/CD
on:
  push:
    branches: [main]
  pull_request:

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
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: ${{ secrets.DEPLOY_KEY }}
      - name: Deploy
        run: |
          ssh -o StrictHostKeyChecking=no wadely@${{ secrets.SERVER_IP }} '
            cd /home/wadely/myapp &&
            git pull &&
            docker compose up -d --build
          '

PR 只跑 test;push 到 main 才部署。

推镜像到 registry 模式

不在服务器上 build(服务器小,build 慢且耗内存):

  1. CI 里 build 镜像,推到 ghcr.io / Docker Hub
  2. ssh 服务器只 docker compose pull && up -d
- uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }}

- uses: docker/build-push-action@v6
  with:
    push: true
    tags: ghcr.io/me/myapp:${{ github.sha }},ghcr.io/me/myapp:latest

部署失败怎么办

  • 零停机滚动:用 docker compose 加健康检查 + Nginx 反代 → 旧容器健康才退
  • 简单回滚git revert + push,CI 自动部署上一版
  • 加锁:用 GitHub Environment + Required Reviewers 让生产部署需要审批

静态站 / 应用平台不需要这些

  • Cloudflare Pages / Vercel / Netlify:连 git 即自动部署,不用写 workflow
  • Railway / Render:同上

GitHub Actions 主要是给"自托管 VPS"补这块能力。

终点

15 篇读完,你有:

  • 一台初始化好的 VPS
  • 装了 Nginx + HTTPS
  • Docker 跑应用 + 数据库
  • 每天自动备份到对象存储
  • UptimeRobot 监控
  • push 即部署

够你跑大多数个人项目和小型 SaaS 了。

继续深入 → 「公司运维」系列:体系化、可观测、合规、规模化。