Прямой ответ
Два способа получить CI без облачных лимитов и счетов. Первый — локальный прогон workflow утилитой act: те же .github/workflows/*.yml исполняются в Docker на вашей машине за секунды до пуша. Второй — self-hosted runner: агент GitHub Actions, зарегистрированный на вашем железе, выполняет джобы репозитория бесплатно и без ограничений hosted-минут. Оба способа требуют осознанности в безопасности: раннер исполняет произвольный код из пулл-реквестов, поэтому для публичных репозиториев self-hosted раннеры GitHub официально не рекомендует без жёстких мер (изоляция, очереди, fork-PR фильтры). Для личных и приватных репозиториев это рабочий и бесплатный сценарий.
act: workflow на ноутбуке до пуша
# установка: brew install act / choco install act-cli / go install
act -l # список джоб
act push # прогнать workflow для события push
act pull_request -j test # конкретная джоба
Как это работает: каждая джоба поднимается в Docker-контейнере, шаги выполняются как в настоящем Actions (те же рантаймы, тот же контекст). Отличия от облака честные: часть сервисов (services: с сетями) и кэши (actions/cache) работают с ограничениями, secrets подаются через .secrets-файл (и этот файл — в .gitignore, по правилам статьи про секреты). Ценность act — итерация: поправить workflow и увидеть результат за 30 секунд, не сжигая hosted-минуты на отладку yml-синтаксиса пушами.
Self-hosted runner: регистрация и работа
- В репозитории: Settings → Actions → Runners → New self-hosted runner — GitHub сгенерирует токен и команды.
- На своей машине:
mkdir actions-runner && cd actions-runner
curl -o runner.tar.gz -L <url-архива из инструкции>
tar xzf runner.tar.gz && ./config.sh --url --token <TOKEN>
./run.sh # или установка как служба (svc.sh install)
- В workflow добавьте
runs-on: self-hosted(или меткуruns-on: [self-hosted, linux]).
Ресурсы: сам агент невесом (десятки МБ); нагрузку создают джобы. Для джоб с контейнерами нужен Docker; джобы исполняются в рабочем каталоге раннера (_work) — держите его вне синхронизируемых папок и с запасом диска (сборочные артефакты живут там же). На Windows раннер работает службой под отдельной учёткой — не давайте ему админ-прав.
Безопасность: правила, которые нельзя пропускать
- Публичные репозитории: любой может открыть fork-PR, и его код попадёт на ваш раннер. Если очень нужно — включайте
Require approval for first-time contributorsи workflow-approval, лучше — не используйте self-hosted для публичных репо вовсе (официальная рекомендация GitHub). - Эфемерность:
./run.sh --ephemeral— раннер живёт одну джобу; в связке с контейнеризацией джоб снижает накопление состояния. - Изоляция: идеальный вариант — раннер внутри одноразовой VM/контейнера, а не на рабочей машине с вашими SSH-ключами и
.envпроектов. Минимальный вариант — отдельный пользователь ОС без sudo. - Секреты репозитория попадают в окружение джоб — раннер на личном ноутбуке фактически получает к ним доступ: для приватных репо с чувствительными credentials оцените риск.
- Сеть: раннер инициирует исходящие соединения к GitHub; входящие не требуются — не открывайте порты наружу.
Когда self-hosted оправдан, а когда нет
Оправдан: приватные проекты с тяжёлыми сборками (hosted-минуты дорогие), специфическое железо (ARM, GPU для ML-тестов), офлайн-инфраструктура, обучающие стенды. Не оправдан: нерегулярные джобы (проще hosted free tier приватных репо), публичные open-source (риск), критичные деплои без резервного раннера (ваш ноутбук выключился — CI встал). Разумный гибрид: hosted для PR из форков, self-hosted — для внутренних веток.
Если же цель — полностью автономный стек без GitHub, связка Gitea + Gitea Actions даёт совместимый с Actions раннер локально; образы для джоб при этом удобно собирать по multi-stage-схемам из статьи про оптимизацию Docker-образов.
FAQ
Бесплатен ли self-hosted runner полностью? Да: лимиты минут GitHub Actions касаются hosted-раннеров; свои джобы вы оплачиваете только электричеством. act использует ваши локальные Docker-образы (базовые образы node/ubuntu качаются один раз).
Можно ли зарегистрировать один раннер на несколько репозиториев? Да — на уровне организации или аккаунта; джобы из разных репо будут выстраиваться в очередь на одном раннере последовательно. Для параллелизма регистрируйте несколько агентов.
Почему джоба висит в очереди «Waiting for a runner»?
Раннер офлайн (проверьте ./run.sh/службу), не совпадают метки runs-on, или все раннеры заняты. Частая причина — метка ubuntu-latest в workflow: у self-hosted её нет, используйте [self-hosted, linux].
Подойдёт ли Raspberry Pi в качестве раннера? Для лёгких джоб (линт, тесты скриптов) — да, есть официальные ARM64-сборки агента. Тяжёлые сборки на Pi будут идти кратно дольше ноутбука.
Эксплуатация: обновление, метки, несколько агентов
Раннер — не «поставил и забыл». GitHub периодически требует обновления агента (обычно он обновляется сам при старте джобы, но при зависших версиях — вручную: ./config.sh remove и повторная регистрация). Полезные приёмы: метки (--labels gpu,linux,arm64) маршрутизируют джобы на нужную машину — так GPU-тесты уезжают на один раннер, а линт на другой; несколько агентов на одной машине регистрируются в отдельных каталогах и работают параллельно (соразмерно CPU/RAM — каждый параллельный джоб это полноценная сборка); --ephemeral в связке со systemd-юнитом даёт паттерн «одна джоба — один свежий процесс».
Мониторинг простой: страница Settings → Actions → Runners показывает статус (Idle/Active/Offline) и время последней джобы. Если раннер часто Offline — проверьте спящий режим ноутбука: для постоянно доступного CI машина не должна засыпать (на Windows — настройки питания, на Linux — systemctl mask sleep.target), либо перенесите агента на сервер/Raspberry Pi. И помните про TTL-логику диска: рабочий каталог _work чистится между джобами частично — закладывайте 20–30 ГБ и периодическую ручную чистку устаревших workspace'ов.
Что делать, если джоба «зависла» на раннере?
Смотрите лог агента: чаще всего джоба ждёт интерактивный ввод (забытый флаг -y) или длинный таймаут сервиса. Отмена — кнопкой в веб-интерфейсе Actions; при мёртвом процессе перезапустите агента. Профилактика: timeout-minutes в каждом workflow.
Можно ли использовать раннер для деплоя на прод? Технически да, но это превращает ваш ноутбук в часть продакшен-контура со всеми рисками доступности. Разумнее: раннер собирает артефакт, а деплой делает отдельная защищённая среда.
Источники: документация GitHub Actions (self-hosted runners, security hardening), проект act (ссылки в шапке).