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 и свой сервер по ключевым параметрам:
| Параметр | Облачный раннер GitHub | Self-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 ВАШ_ТОКЕН
Скрипт задаст несколько вопросов:
- Runner group — Enter (группа по умолчанию);
- Runner name — понятное имя, например
vps-moscow-1; - Labels — метки для выбора раннера в workflow. К стандартным
self-hosted, Linux, X64можно добавить свои, напримерdockerилиprod; - 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, безлимитный трафик и выдача за минуту.