Operations 7 min read

A More Stable GitHub Actions Deployment Solution: Replacing Jenkins and MCP

The author compares four cumbersome blog‑deployment methods—Jenkins, manual CLI, unstable MCP service, and direct SSH—and then details a reliable GitHub Actions workflow that builds the site, caches npm, and deploys it to an Ubuntu server via SSH and rsync.

Wukong Talks Architecture
Wukong Talks Architecture
Wukong Talks Architecture
A More Stable GitHub Actions Deployment Solution: Replacing Jenkins and MCP

I maintain a personal blog at passjava.cn where new articles are published. Previously I tried four approaches to push updates to the server, each with significant drawbacks:

Method 1: Use Jenkins to package and deploy – requires multiple steps.

Method 2: Upload via local command line – adds an extra manual step.

Method 3: Call the MCP service – unstable, sometimes succeeds and sometimes fails.

Method 4: Log into the remote server, pull the latest GitHub code and run build – depends on a Node environment and frequently fails to install npm packages.

Looking for a better solution, I turned to GitHub Actions.

How GitHub Actions Works

When code is pushed to GitHub, the Action detects the commit and starts a job defined in a workflow file.

Preparation on the Ubuntu Server

# Ensure the directory exists and is writable
sudo mkdir -p /nfs-data/passjava/passjava-learning
# Change ownership to the ubuntu user (no sudo needed later)
sudo chown -R ubuntu:ubuntu /nfs-data/passjava/passjava-learning
# Verify permissions
ls -la /nfs-data/passjava/

Configure Password‑less SSH

Generate a deployment key pair (press Enter for all prompts):

$ ssh-keygen -t ed25519 -C "github-deploy" -f ~/.ssh/github-deploy

Add the public key to authorized_keys on the server:

sudo sh -c 'cat ~/.ssh/github-deploy.pub >> ~/.ssh/authorized_keys'

Copy the private key content for later use:

cat ~/.ssh/github-deploy

Add Repository Secrets

In GitHub go to Settings → Secrets and variables → Actions and create three secrets: SERVER_IP – your server’s public IP. SSH_USER – set to ubuntu (fixed value). SSH_PRIVATE_KEY – the full private‑key text copied above.

Create the Workflow File

Place .github/workflows/deploy.yml in the project root with the following content:

name: Build and Deploy Docs
on:
  push:
    branches: [main]  # trigger on pushes to main
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      # 1. Checkout code
      - name: Checkout code
        uses: actions/checkout@v4

      # 2. Set up Node.js
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "22"

      # 3. Cache npm dependencies
      - name: Cache dependencies
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      # 4. Install dependencies
      - name: Install dependencies
        run: npm install

      # 5. Build static site
      - name: Build docs
        run: npm run docs:build

      # 6. Deploy to Ubuntu server via rsync
      - uses: webfactory/[email protected]
        with:
          ssh-private-key: "${{ secrets.SSH_PRIVATE_KEY }}"
      - name: Deploy to Ubuntu Server
        uses: easingthemes/ssh-deploy@main
        env:
          SSH_PRIVATE_KEY: "${{ secrets.SSH_PRIVATE_KEY }}"
          REMOTE_HOST: "${{ secrets.SERVER_IP }}"
          REMOTE_USER: "${{ secrets.SSH_USER }}"
          SOURCE: "dist/"
          TARGET: "/nfs-data/passjava/passjava-learning/dist"
          ARGS: "-avz --delete"

Rsync Options Explained

--delete

: Remove files on the target that no longer exist locally, achieving a full mirror.

Omit --delete if you want to keep extra files on the server.

The ssh-deploy action runs rsync with the specified arguments, allowing you to choose between different synchronization strategies (add‑only, full mirror, add‑without‑overwrite, update‑only, etc.).

Result

After committing the workflow, GitHub Actions executes the steps, builds the documentation, and synchronizes the dist/ directory to the remote server. The site becomes available at www.passjava.cn.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CI/CDdeploymentrsyncSSHGitHub ActionsUbuntustatic site
Wukong Talks Architecture
Written by

Wukong Talks Architecture

Explaining distributed systems and architecture through stories. Author of the "JVM Performance Tuning in Practice" column, open-source author of "Spring Cloud in Practice PassJava", and independently developed a PMP practice quiz mini-program.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.