Woodpecker CI на сервере: частые ошибки и решения
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 — тишина. Порядок диагностики:
- Откройте настройки репозитория в git-провайдере → Webhooks и проверьте, что вебхук на адрес
https://ci.example.com/api/hookвообще создан (Woodpecker создаёт его сам при активации репо, но при сменеWOODPECKER_HOSTзадним числом старый вебхук остаётся указывать на старый адрес). - Посмотрите вкладку Deliveries/История доставок у вебхука — если там 4xx/5xx, причина на стороне сервера Woodpecker, если запрос вообще не уходит — проблема в сети между git-провайдером и вашим сервером (например, сервер за NAT без проброшенного порта).
- Проверьте, что репозиторий активирован в Woodpecker (переключатель в списке репо) — банально, но именно это чаще всего забывают после первого логина.
- Если используете 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 ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →