MAATRIX / Блог / CI/CD на VPS через GitHub Actions: self-hosted runner и rsync

CI/CD на VPS через GitHub Actions: self-hosted runner и rsync

CI/CD на VPS через GitHub Actions: self-hosted runner и rsync-деплой
Блог MAATRIX · 2026-07-07

GitHub Actions умеет собирать и выкатывать код прямо на ваш VPS. Разберём два рабочих подхода: self-hosted runner для сборки на сервере и лёгкий rsync-деплой по SSH с облачных раннеров.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Два подхода к деплою

Есть два способа выкатывать код на VPS из GitHub Actions, и выбор зависит от того, где происходит сборка.

  • Self-hosted runner — агент GitHub на самом VPS. Сборка и тесты идут прямо на сервере, деплой сводится к перемещению файлов и рестарту сервиса. Удобно для тяжёлых сборок и приватных данных.
  • rsync/SSH с облачного раннера — сборка на бесплатных раннерах GitHub, а на VPS попадает только готовый артефакт. Ничего лишнего на сервере не крутится.

Для стабильной сборки важен быстрый диск и предсказуемое CPU: на AMD EPYC + NVMe у MAATRIX self-hosted runner собирает проект заметно быстрее, чем на дешёвом OpenVZ с плавающей нагрузкой.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.

Арендовать VPS для CI/CD

Вариант 1: self-hosted runner

В репозитории откройте Settings → Actions → Runners → New self-hosted runner, выберите Linux x64 и скопируйте команды. На VPS создайте отдельного пользователя и разверните агента.

adduser --disabled-password --gecos "" runner
su - runner
mkdir actions-runner && cd actions-runner
curl -o actions-runner.tar.gz -L \
  https://github.com/actions/runner/releases/download/v2.319.1/actions-runner-linux-x64-2.319.1.tar.gz
tar xzf actions-runner.tar.gz
./config.sh --url https://github.com/USER/REPO --token <TOKEN>

Токен из веб-интерфейса живёт около часа — если конфиг ругается на авторизацию, сгенерируйте новый. Дальше поставьте раннер как systemd-сервис, чтобы он поднимался после перезагрузки.

sudo ./svc.sh install runner
sudo ./svc.sh start
sudo ./svc.sh status

Workflow для self-hosted runner

Файл .github/workflows/deploy.yml. Раннер уже на сервере, поэтому деплой — это git pull, установка зависимостей и рестарт.

name: deploy
on:
  push:
    branches: [ main ]
jobs:
  deploy:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: |
          npm ci
          npm run build
      - name: Restart service
        run: sudo systemctl restart myapp

Чтобы systemctl restart работал без пароля, добавьте пользователю точечное правило в sudoers через visudo -f /etc/sudoers.d/runner:

runner ALL=(ALL) NOPASSWD: /bin/systemctl restart myapp

Вариант 2: rsync-деплой по SSH

Сборка идёт на облачном раннере, на VPS уезжает только каталог dist. Сгенерируйте отдельный SSH-ключ для деплоя и положите публичную часть в ~/.ssh/authorized_keys на сервере, а приватную — в секреты репозитория (SSH_KEY, HOST, USER).

ssh-keygen -t ed25519 -C "gh-deploy" -f deploy_key -N ""
name: deploy
on: { push: { branches: [ main ] } }
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build
      - name: rsync to VPS
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan ${{ secrets.HOST }} >> ~/.ssh/known_hosts
          rsync -az --delete ./dist/ \
            ${{ secrets.USER }}@${{ secrets.HOST }}:/var/www/app/

Флаг --delete удаляет на сервере файлы, которых больше нет в сборке — так каталог всегда точно повторяет артефакт. Root-доступ на VPS MAATRIX позволяет сразу настроить права и пользователя деплоя без запросов в поддержку.

Отдельный совет: не деплойте под root. Заведите пользователя deploy с доступом только к каталогу приложения и точечным правом на рестарт сервиса. Так утечка деплой-ключа не даст злоумышленнику весь сервер, а лишь ограниченную роль. Ключ деплоя держите отдельным от вашего личного SSH-ключа и при компрометации меняйте только его.

После деплоя полезно дёрнуть уведомление в мессенджер — тогда команда сразу видит, какой коммит уехал в прод. В GitHub Actions это делается дополнительным шагом с curl на вебхук Telegram или Slack.

Частые ошибки

  • Раннер offline после ребута — забыли svc.sh install, агент запускался только вручную.
  • Permission denied при рестарте — нет правила в sudoers или опечатка в имени сервиса.
  • Host key verification failed — не выполнили ssh-keyscan, ключ хоста неизвестен раннеру.
  • rsync снёс лишнее--delete с неверным путём назначения. Всегда указывайте конечный слэш в путях.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.

Арендовать VPS для CI/CD

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

Self-hosted runner безопасен для публичного репозитория?

Нет. Для публичных репозиториев self-hosted runner опасен: чужой PR может выполнить код на вашем сервере. Используйте его только в приватных репозиториях.

Сколько ресурсов ест раннер в простое?

В простое агент почти не потребляет ресурсов — несколько десятков мегабайт RAM. Нагрузка появляется только на время сборки.

Можно ли деплоить без пересборки на сервере?

Да, вариант с rsync именно для этого: сборка на облачном раннере, на VPS уходит готовый артефакт.