Github

installation

cli

usage

gh repo

create interactively

gh repo create

github pages

uses jekyll

setup

  • Settings>Pages

    • “Deploy from a branch” main
    • Click the Save button.
  • create ”./_config.yml”

    example

theme: minima
title: dev notes
author: elliot reed
description: notes

site

  • create ”./index.md” or ”./README.md”

    • add homepage content
  • jekyll, create blog with files named “_posts/YYYY-MM-DD-title.md”

  • include title and date

    ---
    title: "YOUR-TITLE"
    date: YYYY-MM-DD
    ---

Actions for VPS

1. Generate Deploy SSH Key on VPS

ssh-keygen -t ed25519 -C "github-actions-vps-deploy" -f ~/.ssh/github_vps_deploy
cat ~/.ssh/github_vps_deploy.pub >> ~/.ssh/authorized_keys
cat ~/.ssh/github_vps_deploy

Copy the private key output.

2. Add Secrets to GitHub Repository

Settings → Secrets and variables → Actions → New repository secret

Add three secrets:

  • VPS_SSH_KEY - The private key from above
  • VPS_HOST - Your VPS IP address
  • VPS_USER - Your VPS username

3. Create Workflow File

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

For Node.js Apps:

name: Deploy to VPS

on:
  push:
    branches: [ main ]
  workflow_dispatch:

env:
  VPS_HOST: ${{ secrets.VPS_HOST }}
  VPS_USER: ${{ secrets.VPS_USER }}
  APP_NAME: your-app-name
  PROCESS_NAME: your-process-name
  NODE_VERSION: "24"

jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    
    steps:
      - name: Set remote path
        run: echo "REMOTE_APP_PATH=~/apps/${{ env.APP_NAME }}" >> $GITHUB_ENV

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

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

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

      - name: Add SSH host to known_hosts
        run: ssh-keyscan ${{ env.VPS_HOST }} >> ~/.ssh/known_hosts

      - name: Install dependencies
        run: npm ci

      - name: Build project
        run: npm run build

      - name: Deploy via rsync
        run: |
          rsync -avz --delete \
            --exclude 'node_modules' \
            --exclude '.git' \
            --exclude '.github' \
            --exclude '.env*' \
            --exclude '*.log' \
            ./dist/ package.json package-lock.json \
            ${{ env.VPS_USER }}@${{ env.VPS_HOST }}:${{ env.REMOTE_APP_PATH }}/

      - name: Install dependencies and restart
        run: |
          ssh ${{ env.VPS_USER }}@${{ env.VPS_HOST }} << 'ENDSSH'
            cd ${{ env.REMOTE_APP_PATH }}
            npm ci --omit=dev
            pm2 delete ${{ env.PROCESS_NAME }} || true
            NODE_ENV=production pm2 start main.js --name ${{ env.PROCESS_NAME }}
            pm2 save
          ENDSSH

      - name: Deployment summary
        run: |
          echo "🚀 Deployed to ${{ env.VPS_HOST }}"
          echo "📁 App: ${{ env.APP_NAME }}"

For Static Sites:

name: Deploy Static Site to VPS

on:
  push:
    branches: [ main ]
  workflow_dispatch:

env:
  VPS_HOST: ${{ secrets.VPS_HOST }}
  VPS_USER: ${{ secrets.VPS_USER }}
  SITE_NAME: your-site-name
  NODE_VERSION: "24"

jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    
    steps:
      - name: Set remote path
        run: echo "REMOTE_SITE_PATH=~/apps/${{ env.SITE_NAME }}" >> $GITHUB_ENV

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

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

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

      - name: Add SSH host to known_hosts
        run: ssh-keyscan ${{ env.VPS_HOST }} >> ~/.ssh/known_hosts

      - name: Install dependencies
        run: npm ci

      - name: Build project
        run: npm run build

      - name: Deploy via rsync
        run: |
          rsync -avz --delete \
            --exclude 'node_modules' \
            --exclude '.git' \
            --exclude '.github' \
            --exclude 'src' \
            ./dist/ \
            ${{ env.VPS_USER }}@${{ env.VPS_HOST }}:${{ env.REMOTE_SITE_PATH }}/

      - name: Deployment summary
        run: |
          echo "🚀 Deployed to ${{ env.VPS_HOST }}"
          echo "📁 Site: ${{ env.SITE_NAME }}"

4. Customize Variables

Update these in the workflow file:

  • APP_NAME / SITE_NAME - Your project directory name
  • PROCESS_NAME - PM2 process name (Node apps only)
  • NODE_VERSION - Node.js version to use for build

5. Deploy

Push to main branch or manually trigger from GitHub Actions tab.

Notes

  • Workflows run on every push to main branch
  • workflow_dispatch allows manual triggering from GitHub UI
  • Node apps use PM2 for process management
  • Static sites just sync files, no runtime process needed

Actions

Automating React App Deployment to Shared Hosting with GitHub Actions

Overview

This document outlines the steps to automate the deployment of a React app from GitHub to shared hosting using rsync over SSH.


1️⃣ Set Up SSH Access

  1. Generated an SSH key for GitHub Actions:
    ssh-keygen -t rsa -b 4096 -f ~/.ssh/github-actions-key -N ""
  2. Added the public key (~/.ssh/github-actions-key.pub) to the hosting server under ~/.ssh/authorized_keys via cPanel SSH Key Manager (preferred method).

To verify the added keys, run:

ssh-add -l

To display the key values in the console, run:

Public key (on the host)

cat ~/.ssh/github-actions-key.pub

Private key for Github

cat ~/.ssh/github-actions-key
  1. Configured SSH access:
    • Verified SSH connection using:
      ssh -p {port and login}
    • Tested scp for manual deployment, but rsync was not available locally.

2️⃣ Configured GitHub Actions Workflow

  1. Created the workflow file:

    mkdir -p .github/workflows
    nano .github/workflows/deploy.yml
  2. Added the following YAML configuration:

    name: Deploy to Shared Hosting
    
    on:
      push:
        branches:
          - main  # Runs when pushing to main branch
    
    jobs:
      deploy:
        runs-on: ubuntu-latest
    
        steps:
          - name: Checkout Repository
            uses: actions/checkout@v4
    
           - name: Setup SSH key for deployment
             uses: webfactory/ssh-agent@v0.5.3
             with:
               ssh-private-key: ${{ secrets.DEPLOY_SSH_KEY }}
    
             - name: Install dependencies
               run: npm install
    
             - name: Build project
               run: npm run build
    
             - name: Test SSH connectivity
               run: ssh -o StrictHostKeyChecking=no -p {port} {identifier} "echo 'SSH connection successful'"
    
             - name: Ensure target directory exists
               run: ssh -p {port} {identifier} "mkdir -p {path}"
    
             - name: Deploy via rsync
               run: rsync -avz --delete --exclude '.well-known' --exclude 'cgi-bin' -e "ssh -p {port}" ./public/ {identifier}:{path}

    —delete will remove files and folders

    —exclude to ensure not deleted, check for all files that should stay (.htaccess, .env)

    -p port port number for hostings port

    ./public/ - the folder containing the contents to be transfered (could be “dist” or “build)

    identifier (hosting login identifier, accountname#root site)

    path (eg: /home/elliotre/site folder)

  3. Saved the file and committed it:

    git add .github/workflows/deploy.yml
    git commit -m "Add GitHub Actions deployment workflow"
    git push origin main

3️⃣ Added SSH Key to GitHub Secrets

  1. Went to GitHub → Repository → Settings → Secrets and variables → Actions.
  2. Created a new secret DEPLOY_SSH_KEY.
  3. Pasted the private SSH key
~/.ssh/github-actions-key

4️⃣ Testing the Deployment

  1. Made a small change in the React app.
  2. Committed and pushed:
    git add .
    git commit -m "Test rsync auto-deploy"
    git push origin main
  3. GitHub Actions built and deployed the app successfully! 🎉

🎯 Final Outcome

✅ Automatic deployment to shared hosting on every push to main
✅ Uses rsync to only update modified files
✅ Deletes old files to keep the deployment clean (optional: remove --delete to disable)
✅ No need for manual file uploads—GitHub handles everything! 🚀