• Home
  • Cloud VPS
    • Hong Kong VPS
    • US VPS
  • Dedicated Servers
    • Hong Kong Servers
    • US Servers
    • Singapore Servers
    • Japan Servers
  • Company
    • Contact Us
    • Blog
logo logo
  • Home
  • Cloud VPS
    • Hong Kong VPS
    • US VPS
  • Dedicated Servers
    • Hong Kong Servers
    • US Servers
    • Singapore Servers
    • Japan Servers
  • Company
    • Contact Us
    • Blog
ENEN
  • 简体简体
  • 繁體繁體
Client Area

CI/CD to Hong Kong VPS with GitHub Actions: Automated Deploy Guide (2026)

July 17, 2026

Manually SSHing into your Hong Kong VPS to deploy updates is a source of human error, inconsistency, and deployment anxiety. GitHub Actions automates the entire pipeline: run tests, build your application, and deploy to your VPS — triggered automatically on every push to main, with zero manual intervention. This guide sets up a complete CI/CD pipeline from GitHub to a Hong Kong VPS for both traditional server deployments and Docker-based applications.


Architecture


Developer pushes to main branch
        ↓
GitHub Actions Runner (GitHub's servers)
   1. Checkout code
   2. Run tests
   3. Build application
   4. SSH into HK VPS → pull code → restart services
        ↓
Hong Kong VPS (CN2 GIA)
   - Updated application running
   - Zero downtime via PM2 reload / Docker rolling update

Step 1: Create a Deploy User on Your VPS

# On your Hong Kong VPS:
useradd -m -s /bin/bash deploy
mkdir -p /home/deploy/.ssh

# Generate SSH key pair for GitHub Actions (on your local machine)
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/github_deploy -N ""

# Copy PUBLIC key to the VPS
cat ~/.ssh/github_deploy.pub >> /home/deploy/.ssh/authorized_keys
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh

# Allow deploy user to restart services without password
echo "deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart myapp, /bin/systemctl reload nginx, /usr/bin/docker compose" \
  >> /etc/sudoers.d/deploy

Step 2: Add Secrets to GitHub Repository

In your GitHub repository: Settings → Secrets and variables → Actions → New repository secret

  • VPS_HOST — your Hong Kong VPS IP address
  • VPS_USER — deploy
  • VPS_SSH_KEY — contents of ~/.ssh/github_deploy (the PRIVATE key)
  • VPS_PORT — your SSH port (e.g., 22222)

Step 3: GitHub Actions Workflow for Node.js App

Create .github/workflows/deploy.yml in your repository:

name: Test and Deploy to HK VPS

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    name: Run Tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      - name: Run linter
        run: npm run lint

  deploy:
    name: Deploy to Hong Kong VPS
    needs: test                    # Only deploy if tests pass
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'   # Only deploy from main branch

    steps:
      - name: Deploy via SSH
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          port: ${{ secrets.VPS_PORT }}
          script: |
            set -e

            echo "Pulling latest code..."
            cd /var/www/myapp
            git pull origin main

            echo "Installing dependencies..."
            npm ci --production

            echo "Running database migrations..."
            npm run migrate

            echo "Reloading application (zero downtime)..."
            pm2 reload myapp --env production

            echo "Verifying deployment..."
            pm2 show myapp | grep "status.*online"

            echo "Deployment complete!"

Step 4: Docker-Based Deployment Pipeline

For Docker-based applications, build the image in CI and push to GitHub Container Registry, then pull on the VPS:

name: Build, Push, and Deploy Docker Image

on:
  push:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    outputs:
      image-tag: ${{ steps.meta.outputs.tags }}

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=sha,prefix=sha-
            type=raw,value=latest,enable={{is_default_branch}}

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest

    steps:
      - name: Deploy to Hong Kong VPS
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          port: ${{ secrets.VPS_PORT }}
          envs: REGISTRY,IMAGE_NAME,GITHUB_SHA
          script: |
            set -e

            # Log in to GitHub Container Registry
            echo ${{ secrets.GITHUB_TOKEN }} | \
              docker login ghcr.io -u ${{ github.actor }} --password-stdin

            # Pull new image
            NEW_IMAGE="ghcr.io/$IMAGE_NAME:sha-${GITHUB_SHA::7}"
            docker pull $NEW_IMAGE

            # Update docker-compose.yml to use new image tag
            cd /opt/myapp
            sed -i "s|image: ghcr.io/$IMAGE_NAME:.*|image: $NEW_IMAGE|" docker-compose.yml

            # Rolling update with Docker Compose
            docker compose up -d --no-deps app

            # Wait for health check
            sleep 10
            docker compose ps app | grep "healthy\|Up"

            # Remove old images
            docker image prune -f

            echo "Docker deployment complete: $NEW_IMAGE"

Step 5: Laravel Deployment Pipeline

name: Deploy Laravel to HK VPS

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_DATABASE: testing
          MYSQL_ROOT_PASSWORD: testpass
        options: --health-cmd="mysqladmin ping" --health-interval=10s

    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
          extensions: mbstring, mysql, redis

      - run: composer install --no-dev --optimize-autoloader
      - run: cp .env.testing .env && php artisan key:generate
      - run: php artisan migrate --force
      - run: php artisan test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          port: ${{ secrets.VPS_PORT }}
          script: |
            set -e
            cd /var/www/mylaravel

            git pull origin main
            composer install --no-dev --optimize-autoloader

            php artisan migrate --force
            php artisan config:cache
            php artisan route:cache
            php artisan view:cache
            php artisan event:cache

            php artisan queue:restart
            sudo systemctl reload php8.3-fpm
            echo "Laravel deployment complete"

Step 6: Notifications and Rollback

Slack Deployment Notifications

      - name: Notify Slack on success
        if: success()
        uses: slackapi/slack-github-action@v1.25.0
        with:
          payload: |
            {
              "text": ":white_check_mark: Deploy to HK VPS succeeded: ${{ github.sha }}"
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

      - name: Notify Slack on failure
        if: failure()
        uses: slackapi/slack-github-action@v1.25.0
        with:
          payload: |
            {
              "text": ":x: Deploy to HK VPS FAILED: ${{ github.sha }} — check Actions logs"
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Automatic Rollback on Failure

          script: |
            set -e
            cd /var/www/myapp

            # Save current commit for rollback
            PREVIOUS_COMMIT=$(git rev-parse HEAD)

            git pull origin main
            npm ci --production

            # Test the application starts
            pm2 reload myapp --env production
            sleep 5

            # Health check — rollback if fails
            if ! curl -sf http://localhost:3000/health > /dev/null; then
              echo "Health check failed — rolling back to $PREVIOUS_COMMIT"
              git reset --hard $PREVIOUS_COMMIT
              npm ci --production
              pm2 reload myapp --env production
              exit 1
            fi

            echo "Deployment verified — health check passed"

Step 7: Environment-Specific Deployments

<code">name: Multi-Environment Deploy

on:
  push:
    branches:
      - main       # → Production (HK VPS)
      - staging    # → Staging server

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: ${{ github.ref_name == 'main' && 'production' || 'staging' }}
      url: ${{ github.ref_name == 'main' && 'https://yourdomain.com' || 'https://staging.yourdomain.com' }}

    steps:
      - uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.VPS_HOST }}     # Different secret per environment
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          script: |
            cd /var/www/${{ github.ref_name == 'main' && 'production' || 'staging' }}
            git pull && npm ci && pm2 reload app

GitHub Environments allow separate secrets per environment (production vs staging) and require manual approval before production deployments — add reviewers in Settings → Environments → production → Required reviewers.


Conclusion

GitHub Actions CI/CD to your Hong Kong VPS eliminates manual deployment steps and ensures every push to main automatically tests, builds, and deploys your application with consistent results. The pipeline’s health check and automatic rollback guard against bad deployments reaching users, while Slack notifications keep the team informed of deployment status.

Automate your deploys: Browse Server.HK Hong Kong VPS plans — any plan supports the SSH-based GitHub Actions deployment pattern described here.

Recent Posts

  • Hong Kong VPS vs Cloudflare Workers: Which for Asia-Pacific APIs? (2026)
  • How to Self-Host Gitea on Hong Kong VPS: Private Git Server (2026)
  • WooCommerce for China Market on Hong Kong VPS: Sell Cross-Border in 2026
  • Game Server on Hong Kong VPS: CS2, Valheim, and Minecraft for Asia (2026)
  • Linux Kernel and Sysctl Hardening for Hong Kong VPS Security (2026)

Recent Comments

  1. Hong Kong VPS Uptime and SLA: What 99.9% Uptime Really Means for Your Business (2026) - Server.HK on How to Monitor Your Hong Kong VPS: Uptime, Performance, and Alert Setup Guide (2026)
  2. Best Hong Kong VPS Providers in 2026: Compared by Speed, Routing, and Value - Server.HK on How to Migrate Your Website to a Hong Kong VPS: Zero-Downtime Transfer Guide (2026)
  3. vibramycin injection on How to Choose the Right Hong Kong VPS Plan: A Buyer’s Guide for 2026
  4. allopurinol for gout on CN2 GIA vs BGP vs CN2 GT: What’s the Real Difference for China Connectivity?
  5. antibiotics online purchase on How to Set Up a WordPress Site on a Hong Kong VPS with aaPanel (Step-by-Step 2026)

Knowledge Base

Access detailed guides, tutorials, and resources.

Live Chat

Get instant help 24/7 from our support team.

Send Ticket

Our team typically responds within 10 minutes.

logo
Alipay Cc-paypal Cc-stripe Cc-visa Cc-mastercard Bitcoin
Cloud VPS
  • Hong Kong VPS
  • US VPS
Dedicated Servers
  • Hong Kong Servers
  • US Servers
  • Singapore Servers
  • Japan Servers
More
  • Contact Us
  • Blog
  • Legal
© 2026 Server.HK | Hosting Limited, Hong Kong | Company Registration No. 77008912
Telegram
Telegram @ServerHKBot