If you're running your own servers (AWS EC2, Oracle Cloud OCI, any VPS), you can set up self-hosted GitHub Actions runners for fully automated deployment at zero additional cost.
This is the exact setup I use across all CloudFold Studio client projects and my own infrastructure.
Phase 1: One-Time Server Setup
Step 1: SSH Into Your Server
Connect to your server via SSH.
Step 2: Create a Dedicated Runner User
# Create a user for GitHub Actions sudo adduser github-runner # Add to necessary groups (PM2 access, site directories) sudo usermod -aG www-data github-runner # Switch to this user sudo su - github-runner
Or use your existing deployment user if you have one (e.g., deployment on CloudPanel).
Step 3: Set Password for Deployment User
# Exit to root user exit # Set password sudo passwd deployment # Switch back su - deployment
Step 4: Register the GitHub Runner
For a GitHub Organization:
Navigate to: github.com/organizations/YOUR-ORG/settings/actions/runners
For a personal repository: Navigate to: Repository → Settings → Actions → Runners → New self-hosted runner
Select your OS (Linux), copy the download and configure commands, and run them on your server.
Step 5: Install Runner as a Service
# Install as a system service (auto-starts on boot) sudo ./svc.sh install # Start the service sudo ./svc.sh start # Verify it's running sudo ./svc.sh status # Should show: "active (running)"
Step 6: Set Up Directory Access
Run this as root to give your deployment user access to all site directories:
# Create a shared group groupadd -f deploy-access # Change owning group of all site home directories for homedir in /home/*/; do user=$(basename "$homedir") # Skip system users if [[ "$user" == "cloudpanel" || "$user" == "deployment" || "$user" == "root" ]]; then continue fi echo "Fixing $homedir → group deploy-access" chgrp deploy-access "$homedir" chmod 750 "$homedir" done # Fix htdocs directories (so deployment can write) for htdocs in /home/*/htdocs; do [[ -d "$htdocs" ]] || continue chgrp -R deploy-access "$htdocs" chmod -R 775 "$htdocs" done
Test it immediately:
cd /home/yoursite/htdocs/www.yoursite.com touch .test-deploy && rm .test-deploy
Phase 2: Configure a Site for Deployment
Step 7: Create the Workflow File
In your repository, create .github/workflows/deploy.yml:
name: Deploy Your Site on: push: branches: - main workflow_dispatch: jobs: deploy: runs-on: self-hosted steps: - name: Clean workspace run: | git checkout -- . 2>/dev/null || true git clean -fd 2>/dev/null || true - uses: actions/checkout@v4 with: clean: true - uses: actions/setup-node@v4 with: node-version: '24' cache: 'npm' - run: npm ci - run: npm run build - name: Deploy files run: | rsync -rlptDv --delete \ --exclude 'node_modules' \ --exclude '.git' \ --exclude '.env' \ --no-perms --no-owner --no-group --no-times \ ./ /home/SITE_USER/htdocs/www.YOURDOMAIN.com/ - name: Install production dependencies run: | cd /home/SITE_USER/htdocs/www.YOURDOMAIN.com npm ci --production - name: Restart PM2 run: | cd /home/SITE_USER/htdocs/www.YOURDOMAIN.com pm2 reload your-app-name || pm2 start npm --name "your-app-name" -- start pm2 save - run: echo "✅ Deployed successfully!"
Replace:
SITE_USER→ your server's site userwww.YOURDOMAIN.com→ your actual domainyour-app-name→ your PM2 process name
Step 8: Commit and Push
git add .github/workflows/deploy.yml git commit -m "Add GitHub Actions deployment" git push origin main
Step 9: Watch It Deploy
- Go to your GitHub repo
- Click the Actions tab
- Your workflow should be running
- Click on it to see live logs
If everything is green, you now have automated deployment on every push to main.
Phase 3: Adding More Sites
For each additional site or repository, register the same runner with a new repository:
cd ~/actions-runner ./config.sh --url https://github.com/YOUR-USERNAME/NEW-REPO --token NEW-TOKEN # Use the same runner name — one runner handles multiple repos
Then copy the workflow file to the new repo and change:
- Site user
- Domain
- PM2 process name
My Full DevOps Stack
This is part of my complete self-hosted infrastructure:
- OCI / AWS EC2 — primary compute
- CloudPanel — manages Node.js, WordPress, PHP, static, and reverse proxy sites
- Self-hosted PostgreSQL — primary relational database
- Self-hosted MongoDB — document store for SaaS projects
- Self-hosted Redis + BullMQ — background job queues
- GitHub Actions self-hosted — automated deployment across all projects
The result: zero per-deployment costs, full control over infrastructure, and automated deployments that run on every push.