Self-hosted runner — это машина, которая выполняет задания GitHub Actions под вашим управлением. Свой раннер нужен, когда сборке требуются ARM, GPU, большой кэш, доступ во внутреннюю сеть или специфические пакеты, которых нет на hosted-виртуалках. Взамен вы берёте на себя обновления, безопасность и очистку диска. Ниже — установка на Ubuntu, служба systemd, эфемерные раннеры и правила безопасности.

Когда свой раннер оправдан

Для публичного репозитория GitHub даёт бесплатные минуты, и свой раннер там чаще создаёт риск, чем экономит. Для приватного проекта с тяжёлой сборкой минуты платные, и собственный сервер окупается. Типичные сценарии:

  • сборка под ARM или архитектуры, которых нет у hosted;
  • GPU-тесты и обучение моделей;
  • доступ к базе или registry внутри закрытой сети;
  • большие кэши зависимостей и долгие интеграционные тесты;
  • требования хранить исходники и артефакты на своей площадке.

Подготовка машины

Возьмите отдельную виртуальную машину, а не рабочий сервер с данными. Минимум для средней сборки — 2–4 vCPU, 8 ГБ ОЗУ и 50 ГБ диска; для Docker-сборок добавьте запас. Создайте непривилегированного пользователя runner и установите зависимости.

sudo adduser --disabled-password --gecos "" runner
sudo usermod -aG docker runner
sudo apt update && sudo apt install -y curl git jq unzip
sudo -u runner -H bash -lc 'docker run --rm hello-world'

Членство в группе docker даёт фактически root-доступ к хосту. Если это неприемлемо, используйте rootless Docker, но тогда часть действий в workflow потребует адаптации. Не выдавайте runner sudo без крайней необходимости.

Регистрация раннера

Токен берётся в настройках репозитория или организации: Settings → Actions → Runners → New self-hosted runner. Он живёт около часа, поэтому не сохраняйте его в скриптах. Версию берём из последнего релиза через API.

sudo -u runner -H bash
export RUNNER_VERSION=$(curl -fsSL https://api.github.com/repos/actions/runner/releases/latest | jq -r .tag_name | sed 's/^v//')
mkdir -p ~/actions-runner && cd ~/actions-runner
curl -fsSL -o runner.tar.gz "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz"
tar xzf runner.tar.gz && ./config.sh --url https://github.com/ORG/REPO --token "$TOKEN" --labels self-hosted,linux,x64,fast --unattended

Флаг —unattended отключает вопросы, —replace заменяет раннер с тем же именем, —ephemeral удаляет регистрацию после одного задания. Последний вариант — правильный выбор для недоверенного кода, но требует автоматики для повторной регистрации.

Метки должны быть осмысленными: не ставьте всем раннерам одинаковый набор, иначе задания уйдут не туда. В workflow выбирайте их явно.

jobs:
  build:
    runs-on:
      group: build-pool
      labels: [self-hosted, linux, x64, fast]

Служба systemd

Скрипт svc.sh создаёт юнит, который запускает run.sh от имени указанного пользователя. Запускать раннер вручную в screen не стоит: после перезагрузки он не поднимется.

cd ~/actions-runner
sudo ./svc.sh install runner
sudo ./svc.sh start
journalctl -u 'actions.runner.*' -f

Юнит называется actions.runner.<owner>-<repo>.<runner-name>.service. Логи удобнее смотреть через journalctl, а не через файлы в _diag: там видно, какую задачу взял раннер и на каком шаге она упала.

Изоляция и эфемерность

Раннер выполняет код из workflow на хосте, если задание не обёрнуто в контейнер. Значит, компрометация зависимости или стороннего действия означает доступ к файлам раннера, кэшу и сетевым ресурсам. Практика 2026 года — одноразовые раннеры и минимальные права.

./config.sh --url https://github.com/ORG/REPO --token "$TOKEN" --ephemeral --unattended
./run.sh
./config.sh remove --token "$REMOVE_TOKEN"
sudo ./svc.sh uninstall

Для парка машин используют Actions Runner Controller в Kubernetes: он создаёт под на каждое задание и удаляет его после. Это дороже в настройке, но снимает проблему грязного состояния между сборками. В обычном Docker без оркестратора можно ставить раннер в контейнер и монтировать docker.sock, однако это ослабляет изоляцию.

Безопасность

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

  • отдельная виртуальная машина или контейнер, без личных файлов и ключей;
  • запрет исходящих соединений, кроме нужных registry и API;
  • минимальные permissions в workflow, секреты только через environments;
  • закрепление сторонних actions по полному SHA, а не по тегу;
  • регулярные обновления раннера и базового образа;
  • отдельные группы раннеров для чувствительных задач.
permissions:
  contents: read
jobs:
  build:
    runs-on: [self-hosted, linux]
    steps:
      - uses: actions/checkout@v4

Секреты не попадают в форки автоматически, но это не защищает от вредоносного кода внутри самого репозитория. Ограничьте, какие workflow могут обращаться к окружению production, включите обязательное ревью. Если раннер обслуживает несколько репозиториев, заведите отдельные группы и не смешивайте задачи разного уровня доверия.

Обслуживание и диагностика

Диск заполняется быстрее, чем кажется: рабочая папка _work, Docker-образы, кэш пакетов. Настройте очистку и следите за inode. Если задание зависло, проверьте процессы и свободное место.

df -h / && df -i /
du -sh ~/actions-runner/_work/*
docker system df
sudo journalctl -u 'actions.runner.*' --since "1 hour ago"

Перед обновлением остановите службу, распакуйте новый архив поверх старого и запустите снова; регистрация сохраняется. Подробные логи включаются переменной ACTIONS_RUNNER_DEBUG=true, но не оставляйте её навсегда: журнал быстро растёт. При переезде на другой хост удалите старую запись через config.sh remove, иначе в интерфейсе останется призрачный раннер.

sudo ./svc.sh stop
tar xzf actions-runner-linux-x64-*.tar.gz
sudo ./svc.sh start
./config.sh remove --token "$REMOVE_TOKEN"

Чек-лист

  • Раннер живёт на отдельной машине без личных данных.
  • Пользователь runner без sudo, Docker изолирован или rootless.
  • Токен регистрации не хранится в репозитории и скриптах.
  • Метки описывают возможности, задания выбирают их явно.
  • Для недоверенного кода включён эфемерный режим.
  • Публичный репозиторий не подключён к домашнему хосту.
  • Настроены очистка диска, мониторинг и обновление раннера.

Итог

Свой раннер снимает ограничения hosted-виртуалок и даёт контроль над окружением сборки, но переносит на вас ответственность за безопасность. Начните с отдельной ВМ, обычного пользователя и службы systemd, затем добавьте эфемерность и ограничение сети. Не подключайте такой раннер к публичному репозиторию и не храните на нём секреты дольше одного задания. При аккуратной настройке решение годами ускоряет сборки и не превращается в открытую дверь во внутреннюю сеть.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте