~/wiki / zapusk-i-khosting / github-actions-auto-deploy-vps-ssh-guide

Automatic Deploy on VPS via GitHub Actions – a step-by-step guide

◷ 9 min read 5/31/2026

Main chat

A chat for vibe coders: news, guides, live cases, marketplace, and finding executors.

$ cd section/ $ join vibe dev

You wrote the code, the AI helped, everything works locally. Time to roll out to the server. Open the terminal, connect via SSH, pull git pull, restart the process. And you have to do it every time. Every fix. Every update.

It’s not a workflow – it’s manual labor that eats up time and sooner or later leads to mistakes.

CI/CD (Continuous Integration/Continuous Deployment) is when the server updates itself as soon as you make git push. No SSH manually, no "wait, I'll shut down." It's in main, and in a minute, the new version lives on the market.

In this guide - how to configure exactly that: autodeploy on VPS through GitHub Actions and SSH, from zero to working pipiline.

What's happening "under the hood"

When you configure CI/CD with GitHub Actions, the scheme looks like this:

code
You make a git push in the main
↓
GitHub notes push
↓
GitHub Actions launches runner (virtual machine)
↓
Runner connects to your server via SSH
↓
On the server execute commands: git pull, npm install, restart
↓
The new version of the application works in the

The whole process is automatic, 30-90 seconds, the same every time.

What you need before starting

  • GitHub repository with your project.
  • VPS with Ubuntu 22.04+ (any: Hetzner, DigitalOcean, Timeweb, REG.RU).
  • User on the server with the right to run your application.
  • Basic knowledge of SSH (ability to connect to the server).

Step 1. Create an SSH key for GitHub Actions

GitHub Actions will connect to the server as a separate user. You need your own pair of SSH keys, not the one you use personally.

On our computer (not on the server!) we generate a pair of keys:

bash копировать
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/github_actions_deploy

The team creates two files:

  • ~/.ssh/github_actions_deployPrivate Key (GitHub Secrets)
  • ~/.ssh/github_actions_deploy.pub - Public key (to be sent to the server)

Step 2. Add a public key to the server

Connect to the server and add the public key to the list of authorized:

bash копировать
# On the server: add a public key
echo "Insert content file .pub" >> ~/.ssh/authorized keys
chmod 600 ~/.ssh/authorized keys

The content of the github_actions_deploy.pub file is one long string that begins with ssh-ed25519. Copy the whole thing.

Check that the connection is working:

bash копировать
# On my computer.
ssh -i ~/.ssh/github actions deploy user@your server-ip

If you connect, the keys are configured correctly.

Step 3. Add secrets to GitHub

Go to GitHub: Settings → Secrets and variables → Actions → New repository secret.

Add three secrets:

Имя секрета Значение
SSH_PRIVATE_KEY Содержимое файла github_actions_deploy (приватный ключ, целиком)
SSH_HOST IP-адрес или домен вашего сервера
SSH_USER Имя пользователя на сервере (например, ubuntu или deploy)

GitHub will encrypt these values and never show them in logs – even if someone gets access to the repository, the secrets will remain closed.

Step 4. Create a workflow file

At the root of the repository, create a folder and file:

code
.github/
└── workflows/
    └── deploy.yml

Here is the basic workflow that works for most projects:

yaml копировать
name: Deploy to VPS

on:
push:
branches:
- Main # starts only when the main

jobs:
deploy:
run-on: ubuntu-latest

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

- name: Deploy via SSH
uses appleboy/ssh-action@v1.0.3
with:
host: ${{secrets.SSH HOST}}
username: ${{secrets.SSH USER}}
key: ${{secrets.SSH PRIVATE KEY}}
Script: |
cd /var/www/my-project # the path to the project on the server
git pull origin main # pull the latest changes
npm install -production # set dependencies
pm2 restart my-project # restart process

Replace /var/www/my-project with the actual project path on the server, and my-project in pm2 restart with the name of your pm2 process.

Enter the file and run it in main. GitHub Actions will start automatically.

Watch the deploitation go by

Open the Actions tab in the GitHub repository. You can see each launch: what step is being taken now, the logs of each team, the final status (ый or ый).

If something goes wrong – a red cross, click, see the exact line with the error. No need to guess.

Ready templates for different stacks

Node.js / Express

yaml копировать
script: |
  cd /var/www/my-project
  git pull origin main
  npm install --production
  pm2 restart my-project

Next.js

yaml копировать
script: |
  cd /var/www/my-project
  git pull origin main
  npm install
  npm run build
  pm2 restart my-project

Python / Telegram bot

yaml копировать
script: |
  cd /home/ubuntu/my-bot
  git pull origin main
  source venv/bin/activate
  pip install -r requirements.txt
  systemctl restart my-bot.service

Docker Compose

yaml копировать
script: |
  cd /var/www/my-project
  git pull origin main
  docker compose pull
  docker compose up -d --build

Deployment only with successful tests

If you have tests, run the deploy only when they have passed. This ensures that the broken code does not get to the server:

yaml копировать
name: Test and Deploy

on:
push:
branches:
- main

jobs:
test:
run-on: ubuntu-latest
steps:
- uses actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm install
run: npm test # If tests fall, the deplo will not start

deploy:
Needs: Test # awaits successful completion of job test
run-on: ubuntu-latest
steps:
- uses actions/checkout@v4
- name: Deploy via SSH
uses appleboy/ssh-action@v1.0.3
with:
host: ${{secrets.SSH HOST}}
username: ${{secrets.SSH USER}}
key: ${{secrets.SSH PRIVATE KEY}}
Script: |
cd /var/www/my-project
git pull origin main
npm install --production
pm2 restart my project

Deploy with notification in Telegram

Do you want to receive a message when the deploit is completed – successfully or with an error? Add a step to the end of workflow:

yaml копировать
- name: Notify Telegram
if: always()# sends both success and error
use: appleboy/telegram-action@master
with:
to: ${secrets. TELEGRAM CHAT ID }
Token: ${secrets. TELEGRAM BOT TOKEN }
message: |
Deploy: ${{job.status}}
Repository: ${{github.repository }}
Branch: ${{github.ref name }}
Commit: ${{github.event.head commit.message }}

Add the secrets of TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID to GitHub Secrets – and after each deploy you will receive a report in Telegram.

A separate user for the deploy (correctly)

Connecting as root for deploy is bad practice. The right approach is to create a separate deploy user with minimal rights.

bash копировать
# On the server.
sudo useradd -m -m /bin/bash deploy

# Give the right to restart the necessary services through sudo without a password
echo "deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart my-app"
| sudo tee /etc/sudoers.d/deploy-restart

# Give rights to the project folder
sudo chown -R deploy:deploy /var/www/my-project

After that, in the SSH_USER secret, specify deploy, not root.

What to do if the den falls

“Permission denied (publickey)”

Problem with the SSH key. Check it out

  • SSH_PRIVATE_KEY is the entire private key (including the -----BEGIN...----- and -----END...----- strings).
  • The public key is added to ~/.ssh/authorized_keys on the server.
  • File rights: chmod 600 ~/.ssh/authorized_keys.

Error "Host key verification failed"

GitHub Actions does not know the fingerprint of your server. Add a parameter to the ssh-action:

yaml копировать
with:
host: ${{secrets.SSH HOST}}
username: ${{secrets.SSH USER}}
key: ${{secrets.SSH PRIVATE KEY}}
host key algorithms: +ssh-rsa # or remove strictly if Ubuntu 22 +

Or add a separate SSH_HOST_KEY secret from the fingerprint server (receive: ssh-keyscan ваш-сервер-ip).

Deployed, but the site is not updated

The pm2/systemd is probably not restarted. Check the name of the process:

bash копировать
pm2 list #names of all pm2 processes
systemctl list-units #names of all systemd services

This is a basic pipeline – it covers 90% of the Vibecoder tasks. When the project grows and you need a deploit without a single second of downtime and an instant rollback - see CI/CD для вайбкодинга: деплой без простоя и откат за 1 минуту, where the Blue-Green strategy is dismantled on top of Docker.

Outcome

Autodeployment through GitHub Actions is an investment of 30 minutes once that saves time with each subsequent release. After setting up your work cycle looks like this:

code
wrote the code → git push in a minute the project in prode

You no longer need to remember the order of commands, connect to the server manually or worry that you forgot npm install. Actions does it for you - every time the same.

** Minimum checklist for the first depletion:**

  1. Create an SSH key: ssh-keygen -t ed25519 -f ~/.ssh/github_actions_deploy
  2. Public key in ~/.ssh/authorized_keys on the server
  3. Private key, host, user in GitHub Secrets
  4. Create .github/workflows/deploy.yml with the desired template
  5. Start in main → open the Actions tab → watch how it works
$ cd ../ ← back to Launch and hosting