This is a clean, reusable GitHub Actions to VPS deployment flow you can copy into any project.
Step 1 — Generate SSH key for GitHub Actions
Run this on your local machine:
ssh-keygen -t rsa -b 4096 -f deploy_key -C "github-action-deploy"
You will get:
deploy_key(private key, keep secret)deploy_key.pub(public key)
Do not upload the private key publicly.
Step 2 — Add public key to VPS
Copy the public key:
type deploy_key.pub # Windows
cat deploy_key.pub # Linux/macOS
On the VPS, add it to the deploy user’s authorized keys:
mkdir -p ~/.ssh
echo "PASTE_PUBLIC_KEY_HERE" >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Test SSH access:
ssh -i "deploy_key" user@vps.example.com
Step 3 — Add GitHub repository secrets
Go to GitHub > Repo > Settings > Secrets and Variables > Actions > New repository secret.
Create these secrets:
DEPLOY_SSH_KEY: contents ofdeploy_key(private key)VPS_HOST: VPS IP or domainVPS_USER: SSH usernameVPS_PORT: SSH port (default22)KNOWN_HOSTS: host key (from the next step)
Step 4 — Generate known_hosts
ssh-keyscan -t rsa -p 22 vps.example.com > known_hosts
Copy the generated line from known_hosts into the KNOWN_HOSTS GitHub secret.
Step 5 — Prepare deployment folder on VPS
ssh -i "deploy_key" user@vps.example.com
mkdir -p ~/Projects/my_project
Replace ~/Projects/my_project with your real project directory.
Step 6 — Create deploy.sh on VPS
Create the script:
nano ~/Projects/my_project/deploy.sh
Use this starter script:
#!/usr/bin/env bash
set -e
echo "Starting deployment..."
cd ~/Projects/my_project
# Add project-specific commands here, for example:
# - install dependencies
# - build
# - restart service
echo "Deployment completed successfully."
Make it executable:
chmod +x ~/Projects/my_project/deploy.sh
Step 7 — Install rsync on VPS
apt update
apt install -y rsync
Step 8 — Create GitHub Actions workflow
Create the workflow file:
mkdir -p .github/workflows
nano .github/workflows/deploy.yml
Paste this workflow:
name: Deploy to VPS
on:
push:
branches: ["main"]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Start SSH agent and add key
uses: webfactory/ssh-agent@v0.9.1
with:
ssh-private-key: ${{ secrets.DEPLOY_SSH_KEY }}
- name: Add known_hosts
run: |
mkdir -p ~/.ssh
echo "${{ secrets.KNOWN_HOSTS }}" > ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
- name: Copy files to VPS
run: |
rsync -avz --delete --exclude='.git' ./ ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }}:~/Projects/my_project/
env:
RSYNC_RSH: "ssh -p ${{ secrets.VPS_PORT }} -o StrictHostKeyChecking=yes"
- name: Run remote deploy script
run: |
ssh -p ${{ secrets.VPS_PORT }} ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} "cd ~/Projects/my_project && ./deploy.sh || echo 'No deploy.sh found'"
Commit and push:
git add .github/workflows/deploy.yml
git commit -m "Add generic deploy workflow"
git push origin main
Step 9 — Verify deployment
Open GitHub Actions and confirm the workflow runs on push.
Check logs for:
- successful
rsync - remote
deploy.shexecution
Step 10 — Customize per project
Replace deploy.sh contents with project-specific commands.
Examples:
- Node.js:
npm install,npm run build,pm2 restart - Python:
pip install -r requirements.txt,systemctl restart service - Docker:
docker-compose pull && docker-compose up -d
Step 11 — Optional PM2 setup
npm install -g pm2
pm2 start server.js --name my_project
pm2 startup systemd
pm2 save
Step 12 — Troubleshooting
Permission denied (publickey): checkDEPLOY_SSH_KEYandauthorized_keyson VPS.Host key verification failed: confirmKNOWN_HOSTSmatchesssh-keyscanoutput.rsync: command not found: install rsync on VPS withapt install -y rsync.deploy.sh not found: verify script path and runchmod +x deploy.sh.