January 27, 2026
Auto-Deploy to VPS with GitHub Actions
Manual deployments are tedious. SSH in, pull, build, restart - every single time. Here's how to automate it.
The Setup
We use GitHub Actions to SSH into our VPS and run deploy commands whenever we push to main.
The Workflow
Create .github/workflows/deploy.yml:
name: Deploy to VPS
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to VPS
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.SERVER_IP }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: 22
script: |
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
cd ~/Projects/your-app
git pull origin main
npm install --production=false
npm run build
pm2 restart app-name
The Gotcha: NVM in Non-Interactive SSH
This is the part that trips people up. When GitHub Actions SSHs into your server, it's a non-interactive session. Your .bashrc doesn't load, which means nvm isn't initialized, which means npm and pm2 aren't in your PATH.
The fix is simple - source nvm at the start of your script:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
Without this, your workflow will "succeed" (green checkmark) but nothing actually happens. Ask me how I know.
Required Secrets
Add these in your GitHub repo under Settings → Secrets → Actions:
SERVER_IP- Your VPS IP addressVPS_USER- SSH username (usuallyroot)VPS_SSH_KEY- Your private SSH key (the entire file contents)
The Result
Push to main → GitHub Actions triggers → SSH into VPS → Pull, build, restart → Done.
No more manual deploys. No more "let me SSH in real quick." Just push and forget.