Self-hosted runner GitHub Actions на VPS: настройка

Боты, ИИ и автоматизация
Self-hosted runner GitHub Actions на VPS: настройка

Self-hosted runner GitHub Actions — это ваш собственный сервер, который выполняет CI/CD-задачи вместо облачных машин GitHub. Зачем это нужно? Минуты Actions в приватных репозиториях ограничены тарифом, облачные раннеры стартуют с холодного состояния и заново качают зависимости на каждой сборке, а очередь в часы пик добавляет минуты ожидания. Свой раннер на VPS снимает все три проблемы: безлимитные минуты за фиксированную цену сервера, тёплые кеши между запусками и мгновенный старт. В этой статье пошагово настроим self-hosted runner на VPS с Ubuntu 24.04: зарегистрируем его через ./config.sh, проверим через ./run.sh и превратим в systemd-сервис через ./svc.sh.

Зачем нужен свой раннер: считаем время и деньги

Сравним облачные раннеры GitHub и свой сервер по ключевым параметрам:

ПараметрОблачный раннер GitHubSelf-hosted на VPS
Минуты сборокОграничены планом (для приватных репозиториев), сверх лимита — доплатаБезлимит за цену сервера
Старт задачиОжидание выделения VM, в пике — очередьМгновенно: раннер уже запущен
Кеш зависимостейЧерез actions/cache: скачивание и распаковка на каждый запускЛежит на диске, доступен сразу
Слои DockerСобираются заново или тянутся из registryЛокальный кеш слоёв сохраняется
ЖелезоСтандартная VM, конфигурацию не выбратьЛюбое: хоть 20 ядер и 32 ГБ RAM

На практике это означает ускорение сборок в 2–5 раз — в основном за счёт тёплых кешей npm/pip/Gradle и локальных слоёв Docker. А по деньгам: активная команда легко сжигает лимит минут за пару недель, тогда как VPS Pro у Cheap-Host (12 vCPU, 16 ГБ RAM, 128 ГБ NVMe) стоит фиксированные 750 ₽/мес — сколько бы сборок вы ни запускали, трафик безлимитный.

Шаг 1. Готовим VPS под раннер

Подойдёт любой сервер с Ubuntu 24.04 и исходящим доступом в интернет — раннер сам опрашивает GitHub по HTTPS, поэтому открывать входящие порты не нужно. Начнём с обновления системы и создания отдельного пользователя (запускать раннер под root нельзя — config.sh откажется работать, и это правильно):

sudo apt update && sudo apt upgrade -y
sudo adduser --disabled-password --gecos "" runner
sudo usermod -aG docker runner   # если сборки используют Docker

Группа docker нужна только если в workflow есть сборка образов — сам Docker ставится по инструкции из статьи Docker на VPS. Также поставьте инструменты, которые нужны вашим сборкам: git, компиляторы, Node.js, Python и т.д. Если сервер свежий, пройдитесь по чек-листу первых шагов после покупки VPS — файрвол и SSH-ключи лишними не будут.

Шаг 2. Получаем команды регистрации в GitHub

Откройте репозиторий на GitHub и перейдите: Settings → Actions → Runners → New self-hosted runner. Выберите операционную систему Linux и архитектуру x64.

GitHub покажет готовый набор команд: скачивание архива раннера актуальной версии, проверку контрольной суммы и команду ./config.sh с уже подставленным токеном регистрации. Токен живёт один час — если провозитесь дольше, просто обновите страницу и возьмите новый.

Если раннер нужен сразу нескольким проектам, регистрируйте его на уровне организации: Organization Settings → Actions → Runners — процедура та же, но раннер станет доступен всем репозиториям организации.

Шаг 3. Скачиваем и регистрируем раннер: config.sh

Дальше работаем на VPS под пользователем runner. Команды ниже — тот самый набор со страницы GitHub (версию и токен берите со своей страницы, они регулярно обновляются):

sudo su - runner
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64.tar.gz -L https://github.com/actions/runner/releases/latest/download/actions-runner-linux-x64-<VERSION>.tar.gz
tar xzf ./actions-runner-linux-x64.tar.gz

Теперь регистрация. Подставьте URL своего репозитория (или организации) и токен со страницы GitHub:

./config.sh --url https://github.com/OWNER/REPO --token ВАШ_ТОКЕН

Скрипт задаст несколько вопросов:

  1. Runner group — Enter (группа по умолчанию);
  2. Runner name — понятное имя, например vps-moscow-1;
  3. Labels — метки для выбора раннера в workflow. К стандартным self-hosted, Linux, X64 можно добавить свои, например docker или prod;
  4. Work folder — Enter (каталог _work по умолчанию).

В конце появится √ Runner successfully added — раннер зарегистрирован.

Шаг 4. Первый запуск: run.sh

Проверим раннер в интерактивном режиме:

./run.sh

Вывод должен закончиться строкой:

√ Connected to GitHub
Listening for Jobs

Одновременно на странице Settings → Actions → Runners раннер появится со статусом Idle (зелёная точка). Он работает: ждёт задачи. Но у ./run.sh есть очевидный минус — процесс привязан к SSH-сессии и умрёт вместе с ней. Останавливаем его (Ctrl+C) и делаем по-взрослому.

Шаг 5. Автозапуск как systemd-сервис: svc.sh

В комплекте с раннером идёт скрипт svc.sh, который устанавливает его как службу systemd — с автозапуском после перезагрузки и автоматическим рестартом при сбоях. Запускается он с sudo из каталога раннера (выйдите из сессии пользователя runner или выполните из-под администратора с указанием пользователя):

cd /home/runner/actions-runner
sudo ./svc.sh install runner
sudo ./svc.sh start

Аргумент runner в команде install — имя пользователя, от которого будет работать служба. Проверяем статус:

sudo ./svc.sh status

Увидите стандартный вывод systemd с active (running). Служба называется по шаблону actions.runner.<owner-repo>.<имя-раннера>.service, поэтому логи доступны и через journalctl:

sudo journalctl -u 'actions.runner.*' -f

Полезные команды управления: sudo ./svc.sh stop — остановить, sudo ./svc.sh uninstall — удалить службу. Как устроены такие юниты изнутри и как писать свои — в статье о создании служб systemd.

Шаг 6. Переключаем workflow на свой раннер

Осталось сказать GitHub Actions использовать ваш сервер. В YAML-файле workflow меняем одну строку — runs-on:

name: CI
on: [push]

jobs:
  build:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: make build
      - name: Test
        run: make test

Если раннеров несколько, уточняйте выбор массивом меток: runs-on: [self-hosted, docker] — задача попадёт только на раннер со всеми перечисленными метками. Сделайте push — на странице Actions задача уйдёт на ваш VPS, а на сервере в каталоге _work появится рабочая копия репозитория.

Безопасность и обслуживание

Несколько правил, которые сэкономят нервы:

  • Не подключайте self-hosted runner к публичным репозиториям. Это официальная рекомендация GitHub: workflow из чужого pull request может выполнить произвольный код на вашем сервере. Для приватных репозиториев с доверенной командой риска нет.
  • Отдельный пользователь и минимум прав. Мы не зря создали пользователя runner — не давайте ему sudo без пароля и доступ к чужим каталогам.
  • Базовая защита сервера. Файрвол, SSH по ключам, fail2ban — стандартный набор из чек-листа безопасности VPS. Входящие порты для раннера открывать не нужно.
  • Обновления раннера. Раннер обновляется автоматически при выходе новых версий — вручную следить не нужно, но systemd-служба должна работать постоянно.
  • Чистка диска. Каталог _work и Docker-кеши растут. Настройте периодический docker system prune -f и следите за свободным местом через df -h.

Какой VPS выбрать под раннер

CI-нагрузка — пиковая: раннер простаивает, потом сборка загружает все ядра. Поэтому важны выделенные ресурсы (KVM, не оверселлинг) и быстрый диск — компиляция и распаковка зависимостей упираются именно в него. Ориентиры:

  • Лёгкие сборки (линтеры, юнит-тесты, небольшие проекты) — хватит 4–6 vCPU и 8 ГБ RAM;
  • Типичный проект с Docker-сборками и параллельными тестами — 8–12 vCPU и 16 ГБ RAM;
  • Монорепозиторий, тяжёлая компиляция — 16+ vCPU и 32 ГБ RAM.

У Cheap-Host под это подходит тариф Pro (12 vCPU, 16 ГБ RAM, 128 ГБ NVMe — 750 ₽/мес), а для тяжёлых сборок — Ultra (20 vCPU, 32 ГБ RAM, 192 ГБ NVMe — 1500 ₽/мес). KVM-виртуализация гарантирует, что ядра действительно ваши, NVMe ускоряет работу с кешами, а если проект вырастет — тариф апгрейдится без потери данных. Сервер выдаётся за 45–60 секунд: от оплаты до первой зелёной сборки реально пройти за полчаса.

FAQ

Self-hosted runner — это бесплатно?

GitHub не тарифицирует минуты на self-hosted runners — вы платите только за свой сервер. Раннер на VPS за 750 ₽/мес выполняет неограниченное количество минут сборок, тогда как облачные минуты для приватных репозиториев ограничены планом.

Безопасно ли использовать self-hosted runner?

Для приватных репозиториев — да, при базовой гигиене: отдельный пользователь, файрвол, обновления. Для публичных репозиториев GitHub официально не рекомендует self-hosted runners: автор любого pull request потенциально может выполнить код на вашем сервере.

Почему сборки на своём раннере быстрее?

Нет очереди на выделение виртуальной машины, кеши зависимостей и слои Docker сохраняются между запусками прямо на диске, а NVMe и выделенные ядра дают стабильную производительность. На типичных проектах — ускорение в 2–5 раз.

Что делать, если раннер показывает статус Offline?

Проверьте службу: sudo ./svc.sh status и логи journalctl -u 'actions.runner.*' -f. Частые причины: служба не установлена через svc.sh install (раннер умер вместе с SSH-сессией) или нет исходящего HTTPS-доступа к github.com.

Можно ли подключить один раннер к нескольким репозиториям?

Один экземпляр привязывается либо к репозиторию, либо к организации. Раннер уровня организации видят все её репозитории — это удобнее. На одном VPS можно поставить и несколько экземпляров в разных каталогах.

Вывод

Self-hosted runner — это полчаса настройки в обмен на безлимитные минуты CI/CD и сборки в разы быстрее облачных. Вся процедура сводится к четырём действиям: страница Settings → Actions → Runners в GitHub, регистрация через ./config.sh, проверка через ./run.sh и установка systemd-службы через ./svc.sh. Если для раннера нужен сервер — у Cheap-Host тариф Pro от 750 ₽/мес: 12 vCPU, 16 ГБ RAM, NVMe, KVM, безлимитный трафик и выдача за минуту.

Читайте также