Skip to main content

CI/CD Pipeline with GitHub Actions

Overview

CreatedUpdatedStatus
2026-01-282026-01-31✅ Accepted

Context

Automate deployments from GitHub to VPS for Next.js frontend, Hono API, and Docusaurus docs.

Decision: GitHub Actions with SSH deployment. Build Docker images on VPS (not Docker Hub) for simplicity.

Architecture

GitHub Repository
├─ push dev → SSH → Development VPS (app.quangnguyen.dev)
└─ push main → SSH → Production VPS (quangnguyen.com)

Deploy script: git pull → docker compose build → up -d → prune

Workflow

# .github/workflows/deploy.yml
name: Deploy

on:
push:
branches: [dev, main]
workflow_dispatch:

jobs:
deploy-dev:
if: github.ref == 'refs/heads/dev'
runs-on: ubuntu-latest
environment: development
steps:
- uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
script: |
cd /var/www/app_quangnguyen_dev/data/www/app.quangnguyen.dev
git pull origin dev
docker compose -f docker-compose.prod.yml up -d --build
docker image prune -f

deploy-prod:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.PROD_VPS_HOST }}
username: ${{ secrets.PROD_VPS_USER }}
key: ${{ secrets.PROD_VPS_SSH_KEY }}
script: |
cd ${{ secrets.PROD_APP_PATH }}
git pull origin main
docker compose -f docker-compose.prod.yml up -d --build
docker image prune -f

GitHub Secrets

EnvironmentSecrets
DevelopmentVPS_HOST, VPS_USER, VPS_SSH_KEY
ProductionPROD_VPS_HOST, PROD_VPS_USER, PROD_VPS_SSH_KEY, PROD_APP_PATH

Manual Deploy Script

#!/bin/bash
# deploy.sh
set -e
git pull origin main
SERVICE=${1:-"all"}

deploy_service() {
docker compose -f docker-compose.prod.yml build $1
docker compose -f docker-compose.prod.yml up -d --no-deps --wait $1
}

[ "$SERVICE" = "all" ] && { deploy_service api; deploy_service web; } || deploy_service $SERVICE
docker image prune -f

Usage: ./deploy.sh api or ./deploy.sh web or ./deploy.sh

Rollback

git log --oneline -10
git checkout <commit-hash>
docker compose -f docker-compose.prod.yml up -d --build

Alternatives Considered

OptionRejected Because
Docker HubExtra step, registry costs
GitHub Container RegistrySimilar overhead
Kubernetes/ArgoCDOverkill for single-server

Consequences

Positive: Simple setup, fast deploys, easy debugging via SSH Negative: Builds use VPS resources, no image versioning in registry

Security

  • SSH keys in GitHub Secrets (encrypted)
  • Dedicated deploy user with docker group only
  • Never commit .env.prod files