Automated Deployment, the Last Step of CI/CD
The complete CI/CD chain is: commit code → automatic build → automatic test → automatic deployment. The first three steps are CI; the last step is CD.
With GitHub Actions, we can easily achieve “push equals live” — push your code to GitHub and it is built and deployed to the server automatically.
This article uses deploying a static site to an Nginx server as an example to show GitHub Actions’ automatic deployment flow.
The Overall Architecture
Developer pushes code
↓
GitHub Actions triggers the Workflow
↓
Build the project (npm build / jekyll build)
↓
Upload to the server over SSH
↓
Restart Nginx (optional)
↓
Deployment complete
Step 1: Prepare the Server
Create the deployment directory on the server:
sudo mkdir -p /var/www/my-site
sudo chown -R $USER:$USER /var/www/my-site
Configure Nginx:
server {
listen 80;
server_name example.com;
root /var/www/my-site;
index index.html;
}
Step 2: Configure GitHub Secrets
You need three Secrets:
| Secret name | Description |
|---|---|
DEPLOY_HOST |
Server IP or domain name |
DEPLOY_USER |
SSH username |
DEPLOY_KEY |
Contents of the SSH private key |
Settings → Secrets and variables → Actions → New repository secret
Generate an SSH key pair (on the server):
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/github_actions
cat ~/.ssh/github_actions.pub >> ~/.ssh/authorized_keys
cat ~/.ssh/github_actions # copy the contents as DEPLOY_KEY
Step 3: Write the Workflow
name: Deploy to Server
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install and Build
run: |
npm ci
npm run build
- name: Deploy via SCP
uses: appleboy/scp-action@v0.1.7
with:
host: $
username: $
key: $
source: "dist/*"
target: "/var/www/my-site"
strip_components: 1
This Workflow does three things:
- Check out the code and install dependencies
- Build the project
- Upload the build artifacts to the server over SCP
Advanced: Zero-Downtime Deployment
The scheme above overwrites files directly, so there may be a brief inconsistency during deployment. A better approach is zero-downtime deployment:
- name: Deploy with symlink swap
uses: appleboy/ssh-action@v1.0.3
with:
host: $
username: $
key: $
script: |
DEPLOY_DIR="/var/www/my-site/releases/$(date +%Y%m%d%H%M%S)"
mkdir -p $DEPLOY_DIR
# assume the earlier steps have already uploaded the artifacts to a temp directory
cp -r /tmp/deploy/* $DEPLOY_DIR/
ln -sfn $DEPLOY_DIR /var/www/my-site/current
# clean up old versions (keep the most recent 3)
ls -dt /var/www/my-site/releases/* | tail -n +4 | xargs rm -rf
A Simpler Option: GitHub Pages
If your project is a static site, the simplest “automatic deployment” is to deploy straight to GitHub Pages:
name: Deploy to GitHub Pages
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
id-token: write
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- uses: actions/upload-pages-artifact@v3
with:
path: dist
- uses: actions/deploy-pages@v4
Push your code, it builds automatically, and it deploys automatically to https://你的用户名.github.io/仓库名 — completely free, zero server configuration.
Afterword
CI/CD tools each have their own battlefield — Jenkins wins on flexibility and its plugin ecosystem, Gitlab CI excels at gatekeeping scenarios deeply integrated with the code repository, and GitHub Actions ties CI/CD together with the GitHub ecosystem, greatly lowering the barrier to automated deployment. None of them is guaranteed to replace another; the key is your team’s tech stack and actual needs.
The value of automatic deployment isn’t only that it saves you the trouble of manual uploads; more importantly, it makes the deployment process standardized, traceable and rollback-capable. Every change to a line of code can be traced precisely to which deployment and which version.
That is the essence of DevOps — let machines do what machines are good at, and people do what people are good at.
A small step forward every day is a new height!