MAATRIX / Блог / GitLab Runner на VPS: свой CI/CD-агент

GitLab Runner на VPS: свой CI/CD-агент

GitLab Runner на VPS: собственный CI/CD-агент за 15 минут
Блог MAATRIX · 2026-07-07

Свой GitLab Runner на VPS снимает лимиты бесплатных минут и даёт полный контроль над окружением сборки. Поставим агент с Docker-executor, зарегистрируем его и напишем пайплайн с деплоем.

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

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

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

Зачем свой раннер

GitLab.com даёт ограниченный пул CI-минут, а на self-managed инсталляциях раннеров нет вовсе. Собственный агент на VPS решает обе проблемы: сборки идут без лимитов, а окружение вы контролируете полностью.

Executor определяет, где выполняется job. Самый практичный — docker: каждая job стартует в чистом контейнере, ничего не остаётся между запусками. Для этого нужен установленный Docker.

CI-нагрузка пульсирующая и требовательная к диску. NVMe у MAATRIX ускоряет docker pull и распаковку слоёв, а предсказуемое ядро AMD EPYC не даёт сборкам «плавать» по времени. На дешёвых виртуалках с оверселлингом одна и та же сборка то укладывается в минуту, то тянется пять — это убивает предсказуемость пайплайна.

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

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

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

Арендовать VPS для GitLab Runner

Установка Docker и раннера

Ставим Docker из официального скрипта, затем репозиторий GitLab Runner и сам пакет.

curl -fsSL https://get.docker.com | sh
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt install -y gitlab-runner

После установки раннер уже работает как systemd-сервис под пользователем gitlab-runner. Проверить статус:

sudo systemctl status gitlab-runner
sudo gitlab-runner --version

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

Токен регистрации возьмите в GitLab: Settings → CI/CD → Runners → New project runner. Регистрируем в неинтерактивном режиме сразу с docker-executor.

sudo gitlab-runner register \
  --non-interactive \
  --url "https://gitlab.com/" \
  --token "<RUNNER_TOKEN>" \
  --executor "docker" \
  --docker-image "alpine:latest"

Проверьте, что раннер появился в списке и подхватывает jobs:

sudo gitlab-runner list
sudo gitlab-runner verify

.gitlab-ci.yml с кэшем и деплоем

Пайплайн из двух стадий: сборка с кэшированием зависимостей и деплой по SSH. Приватный ключ храните в Settings → CI/CD → Variables как защищённую переменную SSH_PRIVATE_KEY.

stages: [ build, deploy ]

build:
  stage: build
  image: node:20
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths: [ node_modules/ ]
  script:
    - npm ci
    - npm run build
  artifacts:
    paths: [ dist/ ]

deploy:
  stage: deploy
  image: alpine:latest
  only: [ main ]
  script:
    - apk add --no-cache openssh-client rsync
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | ssh-add -
    - rsync -az --delete dist/ deploy@$SERVER:/var/www/app/
cache ускоряет повторные сборки (node_modules переиспользуется), а artifacts передаёт результат сборки в стадию деплоя. Разница принципиальная: cache — необязательная оптимизация, которая может отсутствовать, а artifacts гарантированно доезжают между стадиями. Никогда не полагайтесь на cache как на способ передать критичные файлы дальше по пайплайну.

Переменную SSH_PRIVATE_KEY обязательно пометьте как Protected и Masked. Protected закрывает её от веток и тегов без защиты, Masked прячет значение в логах. Иначе приватный ключ рискует утечь в вывод job при отладке.

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

Docker-executor со временем забивает диск старыми образами и volume. Настройте регулярную очистку по cron:

docker system prune -af --volumes
# в crontab -e:
0 4 * * * /usr/bin/docker system prune -af --volumes

Ограничить одновременные сборки можно в /etc/gitlab-runner/config.toml параметром concurrent — на маленьком VPS ставьте 1–2, чтобы сборки не конкурировали за CPU.

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

  • Runner не берёт jobs — не совпадают теги. Либо снимите теги с job, либо включите «Run untagged jobs» в настройках раннера.
  • Cannot connect to Docker daemon — пользователь gitlab-runner не в группе docker: sudo usermod -aG docker gitlab-runner.
  • Диск переполнен — забыли prune, слои образов копятся бесконечно.
  • Permission denied (publickey) — забыли добавить публичный ключ деплоя в authorized_keys на целевом сервере.

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

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

Арендовать VPS для GitLab Runner

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

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

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

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

Чем docker-executor лучше shell?

Docker-executor даёт чистое изолированное окружение на каждую job — никаких следов между сборками. Shell-executor выполняет всё прямо в системе и легко «загрязняется».

Один раннер на несколько проектов — можно?

Да, зарегистрируйте раннер как групповой или инстансный, и он будет обслуживать несколько проектов сразу.

Сколько RAM нужно раннеру?

Сам агент лёгкий, но сборкам нужна память. Для комфортных Node/Python-сборок берите VPS от 2 ГБ RAM, для тяжёлых образов — больше.