MAATRIX / Блог / Woodpecker CI на сервере: частые ошибки и решения

Woodpecker CI на сервере: частые ошибки и решения

MAATRIX

Woodpecker CI — форк Drone CI, появившийся после того, как Drone сменил лицензию и часть функций ушла за платную подписку. Сообщество подхватило последнюю открытую версию и с тех пор развивает её как полностью open-source CI/CD без ограничений по числу репозиториев и агентов. Инструмент простой и лёгкий, но именно поэтому многие ошибки конфигурации не подсказывают явно, что пошло не так — приходится разбираться по логам. Ниже — конкретные проблемы, с которыми реально сталкиваются при разворачивании Woodpecker на своём сервере, и как их закрыть.

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

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

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

Установка сервера и агента через Docker Compose

Woodpecker состоит из двух компонентов: server (веб-интерфейс, API, хранит пайплайны и статусы) и agent (выполняет шаги пайплайна, обычно через Docker). Их разворачивают раздельными контейнерами даже на одном хосте — это упрощает масштабирование агентов позже.

Минимальный docker-compose.yml:

services:
  woodpecker-server:
    image: woodpeckerci/woodpecker-server:latest
    ports:
      - "8000:8000"
    volumes:
      - woodpecker-server-data:/var/lib/woodpecker/
    environment:
      - WOODPECKER_OPEN=false
      - WOODPECKER_HOST=https://ci.example.com
      - WOODPECKER_ADMIN=your-git-username
      - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}

  woodpecker-agent:
    image: woodpeckerci/woodpecker-agent:latest
    command: agent
    restart: always
    depends_on:
      - woodpecker-server
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WOODPECKER_SERVER=woodpecker-server:9000
      - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
      - WOODPECKER_BACKEND=docker

volumes:
  woodpecker-server-data:

Типичные ошибки на этом шаге:

  • WOODPECKER_HOST без https:// — сервер стартует, но OAuth-редиректы и вебхуки формируются неправильно, git-провайдер откажет в колбэке. Указывайте полный URL со схемой, без завершающего слэша.
  • Один и тот же WOODPECKER_AGENT_SECRET обязателен на сервере и на агенте — это общий ключ для grpc-хендшейка, а не пароль пользователя. Сгенерировать можно так: openssl rand -hex 32.
  • Порт 9000 не проброшен, если агент запускается на отдельном хосте — по умолчанию grpc сервера слушает 9000, а не 8000 (это порт HTTP/веб-интерфейса). Их путают чаще всего.
  • Каталог для woodpecker-server-data должен существовать и принадлежать пользователю, от которого запущен контейнер — иначе SQLite (дефолтная БД) не создаст файл, и сервер падает в рестарт-лупе. Смотрите docker logs woodpecker-server — там прямо видно permission denied.

Если сервер разворачивается с нуля вместе с остальным CI/CD-окружением, полезно заранее прикинуть требования к диску и памяти — базовая настройка VPS под разработчика и CI/CD закрывает это ещё до установки самого Woodpecker.

OAuth-авторизация с Gitea, GitHub и GitLab

Woodpecker не хранит свою базу пользователей — авторизация идёт через OAuth-приложение вашего git-провайдера (Gitea, GitHub, GitLab, Bitbucket, Forgejo). Здесь чаще всего ошибаются с redirect URI.

Для Gitea:

WOODPECKER_GITEA=true
WOODPECKER_GITEA_URL=https://git.example.com
WOODPECKER_GITEA_CLIENT=xxxxxxxx
WOODPECKER_GITEA_SECRET=xxxxxxxx

При создании OAuth-приложения в Gitea в поле Redirect URI нужно указать именно https://ci.example.com/authorize — если написать .../login или забыть слэш в конце пути, провайдер вернёт redirect_uri_mismatch, а Woodpecker покажет невнятную ошибку авторизации без деталей причины.

Частые причины отказа входа:

  • Redirect URI в приложении провайдера не совпадает с WOODPECKER_HOST + /authorize посимвольно (http vs https, наличие www, порт).
  • WOODPECKER_ADMIN должен содержать логин на git-провайдере ровно так, как он отображается там (регистр важен для некоторых форджей) — иначе первый пользователь заходит, но не получает admin-прав и не видит настройки сервера.
  • Для GitHub Enterprise или self-hosted GitLab обязательно указывается WOODPECKER_GITHUB_URL / WOODPECKER_GITLAB_URL — без этого Woodpecker пытается стучаться в публичный github.com или gitlab.com и получает 404 на API-запросах.
  • После смены секрета OAuth-приложения нужно перезапустить контейнер сервера — переменные окружения читаются только при старте, hot-reload нет.

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

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

Арендовать сервер

Агент не подключается к серверу (grpc-ошибки)

Самая частая жалоба в issues и на форумах — агент запускается, но в логах видно бесконечные попытки реконнекта:

grpc: failed to connect: connection refused

или

rpc error: code = Unauthenticated desc = invalid token

Разбор по причинам:

  • connection refused — агент не видит сервер по указанному адресу и порту. Если контейнеры в одной docker-compose сети, используйте имя сервиса (woodpecker-server:9000), а не localhost. Если агент на отдельной машине — проверьте, что 9000 порт открыт в фаерволе (ufw allow 9000/tcp) и что сервер слушает не только 127.0.0.1.
  • Unauthenticated — секреты на сервере и агенте не совпадают дословно, либо в переменной остался перенос строки/пробел при копировании через .env-файл. Проверьте docker exec woodpecker-agent env | grep AGENT_SECRET и сверьте с серверным значением.
  • Агент подключился, но пайплайны не берёт в работу — проверьте WOODPECKER_MAX_WORKFLOWS (по умолчанию агент берёт ограниченное число параллельных заданий) и что агент вообще виден в разделе Agents веб-интерфейса как «online». Если офлайн — смотрите логи агента на предмет обрыва по TLS, если сервер за reverse-proxy с самоподписанным сертификатом.
  • Если сервер стоит за Nginx/Caddy, grpc и обычный HTTP трафик на порт 8000 нужно разделять явно — иначе TLS-терминация на прокси ломает grpc-стрим агента, который ожидает HTTP/2. Для Nginx требуется отдельный location с grpc_pass и включённым HTTP/2 на upstream.

Webhook не приходит, пайплайн не запускается

Пуш в репозиторий проходит, а в Woodpecker — тишина. Порядок диагностики:

  1. Откройте настройки репозитория в git-провайдере → Webhooks и проверьте, что вебхук на адрес https://ci.example.com/api/hook вообще создан (Woodpecker создаёт его сам при активации репо, но при смене WOODPECKER_HOST задним числом старый вебхук остаётся указывать на старый адрес).
  2. Посмотрите вкладку Deliveries/История доставок у вебхука — если там 4xx/5xx, причина на стороне сервера Woodpecker, если запрос вообще не уходит — проблема в сети между git-провайдером и вашим сервером (например, сервер за NAT без проброшенного порта).
  3. Проверьте, что репозиторий активирован в Woodpecker (переключатель в списке репо) — банально, но именно это чаще всего забывают после первого логина.
  4. Если используете self-hosted Gitea/GitLab во внутренней сети без доступа извне, а Woodpecker снаружи — вебхук физически не дойдёт. Здесь либо оба сервиса должны быть в одной сети/VPN, либо стоит держать git-сервер и CI на одном сервере или в одном датацентре.

Пример рабочего nginx-конфига с учётом grpc и webhook-путей:

server {
    listen 443 ssl;
    server_name ci.example.com;

    location /ws/ {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Обратите внимание на location /ws/ — Woodpecker использует WebSocket для live-обновления логов в браузере, и без правильных Upgrade-заголовков лог пайплайна просто не будет обновляться в реальном времени, хотя сама сборка отработает.

Ошибки в .woodpecker.yml

Конфиг пайплайна лежит в корне репозитория как .woodpecker.yml (или несколько файлов в каталоге .woodpecker/). Частые баги:

  • Ключ pipeline: вместо steps: — в версии 2.x синтаксис поменялся, старый ключ pipeline больше не читается молча, шаги просто не выполняются без явной ошибки. Если конфиг переносили со старого Drone или ранней версии Woodpecker — первым делом проверьте это.
  • Отсутствие image: в шаге — каждый step обязан указывать Docker-образ, в котором он выполняется. Без него сборка падает с ошибкой парсинга ещё до старта контейнера.
  • when: условия с опечаткой в имени ветки — например event: push вместо event: [push] в старых версиях парсера воспринимался иначе. Сверяйтесь с документацией конкретной мажорной версии, синтаксис списков в YAML капризен к отступам.
  • Секреты не подставляются — секрет должен быть явно разрешён для репозитория в настройках (Repository Settings → Secrets) и указан в шаге через secrets: [my_secret] либо через переменные окружения from_secret. Просто создать секрет глобально недостаточно, если не включена его доступность для конкретного репо.

Пример рабочего минимального конфига:

steps:
  build:
    image: node:20
    commands:
      - npm ci
      - npm run build

  deploy:
    image: alpine
    commands:
      - echo "deploy step"
    when:
      branch: main
      event: push

Docker socket, права доступа и безопасность агента

По умолчанию агент монтирует /var/run/docker.sock внутрь себя — это самый простой backend (Docker-out-of-Docker, DooD), но у него есть цена: любой, кто может задать шаг пайплайна, фактически получает root-доступ к хост-системе через сокет Docker. Для приватного сервера, где вы единственный пользователь, это приемлемо. Для команды с внешними контрибьюторами — уже риск.

Что стоит учитывать:

  • Ограничивайте доступ к репозиториям в Woodpecker только доверенным пользователям, если используете DooD-агент — pull request от постороннего с вредоносным .woodpecker.yml теоретически может выполнить произвольные команды на хосте.
  • Альтернатива — запускать агент в изолированной VM или на отдельном сервере, который не хранит ничего критичного, и просто пересоздавать его при подозрении на компрометацию.
  • Следите за местом на диске: Docker-образы, которые тянет агент под каждый пайплайн, копятся в /var/lib/docker. Периодическая docker system prune -af --volumes на хосте агента экономит десятки гигабайт, но чистит и кэш слоёв — следующая сборка будет медленнее.
  • Если агентов несколько и они по очереди перетягивают одни и те же тяжёлые образы (например, node, golang, python) — имеет смысл поднять локальный registry-кэш или приватный Docker registry, чтобы не гонять эти слои каждый раз из публичного Docker Hub с его лимитами на анонимные pull.

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

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

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

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

Чем Woodpecker принципиально отличается от Drone CI?

Кодовой базой на момент форка и лицензией — Woodpecker остаётся полностью open-source (Apache 2.0), тогда как в Drone часть функций для командной работы ушла в платную версию Harness. Синтаксис .woodpecker.yml близок к старому Drone, но не идентичен более новым версиям.

Можно ли запустить агент без Docker, например через SSH или Kubernetes-backend?

Да, у Woodpecker есть альтернативные backend'ы (kubernetes, local, ssh), но Docker-backend — самый документированный и предсказуемый вариант для одиночного VPS, поэтому в большинстве инструкций по умолчанию используют именно его.

Почему пайплайн висит в статусе "pending" и не стартует, хотя агент онлайн?

Обычно это лимит WOODPECKER_MAX_WORKFLOWS — если у агента уже занят весь пул параллельных задач, новые встают в очередь. Проверьте количество активных сборок в веб-интерфейсе и при необходимости поднимите значение или добавьте второй агент.

Нужен ли отдельный сервер под Woodpecker или можно на том же, где крутится продакшен?

Технически можно, но сборки потребляют CPU и диск рывками — на слабой конфигурации это заметно скажется на соседних сервисах во время интенсивных пайплайнов. Разумнее держать CI на отдельной машине или хотя бы с явными лимитами ресурсов на агент-контейнер.

Как обновиться на новую версию без потери истории сборок?

Сделайте бэкап volume с данными сервера (SQLite-файл или внешнюю БД, если настроена Postgres/MySQL), затем обновите теги образов в compose-файле и проверьте changelog конкретного релиза на breaking changes в схеме .woodpecker.yml — между мажорными версиями синтаксис пайплайнов иногда меняется.

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

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

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