Как установить и настроить Woodpecker CI на VPS
Drone CI когда-то был одним из самых приятных self-hosted CI/CD решений — пока новые версии не ушли под коммерческую лицензию. Часть комьюнити форкнула последнюю свободную версию и продолжила развивать её как полностью open-source проект под именем Woodpecker CI. Ниже — рабочая установка на чистый VPS: от Docker до первого зелёного пайплайна.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое Woodpecker CI и чем он отличается от Drone
Woodpecker — прямой форк Drone CI, сделанный сообществом после того, как Drone перешёл на Business Source License и часть функциональности закрыл для платной версии. Woodpecker остаётся под Apache 2.0 целиком, включая то, что в Drone стало платным (например, несколько параллельных пайплайнов и продвинутые условия запуска).
Архитектурно это тот же принцип, что и у Drone:
- server — веб-интерфейс, хранит конфигурацию, принимает вебхуки от git-провайдера и раздаёт задания агентам;
- agent — воркер, который реально выполняет шаги пайплайна как отдельные Docker-контейнеры;
- база данных — SQLite из коробки для теста, Postgres или MySQL для боевой нагрузки.
Конфиг пайплайна — YAML-файл .woodpecker.yml в корне репозитория, синтаксис близок к тому, что был у Drone (шаги = сервисы = Docker-образы), но не идентичен один в один — при миграции со старых .drone.yml сверяйтесь с актуальной документацией конкретной версии, которую ставите, потому что формат между релизами Woodpecker местами менялся.
Из коробки поддерживаются GitHub, GitLab, Bitbucket, Gitea и Forgejo как источники репозиториев — это удобно, если у вас уже поднят свой Gitea на том же VPS: получаете полностью автономный стек git + CI без внешних сервисов.
Требования к серверу и подготовка
Для сервера Woodpecker плюс один агент с запасом хватает скромной конфигурации:
| Ресурс | Минимум | Комфортно |
|---|---|---|
| CPU | 1 vCPU | 2 vCPU |
| RAM | 1 ГБ | 2-4 ГБ |
| Диск | 20 ГБ SSD | 40+ ГБ SSD |
| ОС | Ubuntu 24.04 / Debian 12 | — |
Оперативной памяти нужно больше, если пайплайны собирают тяжёлые образы (Node, Java, Rust) — каждый параллельный шаг это отдельный контейнер, и они едят RAM сверх самого Woodpecker. Если планируете 3-4 одновременные сборки, закладывайте от 4 ГБ.
Перед установкой обновите систему и заведите отдельного пользователя без root:
apt update && apt -y upgrade
adduser deploy
usermod -aG sudo deploy
Дальше работаем от deploy, root оставляем для экстренных случаев.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверУстановка Docker и Docker Compose
Woodpecker поставляется официальными Docker-образами, отдельно ставить бинарники не нужно. Если Docker ещё не стоит — разверните его с нуля, шаги для Ubuntu 24.04 расписаны в отдельной статье про установку Docker на VPS. Коротко:
curl -fsSL https://get.docker.com | sh
usermod -aG docker deploy
Перелогиньтесь, чтобы группа docker применилась, и проверьте:
docker --version
docker compose version
Начиная с актуальных версий Docker плагин compose встроен в сам docker (команда docker compose, без дефиса) — отдельно docker-compose ставить не нужно.
OAuth-приложение и docker-compose для Woodpecker
Server и agent общаются между собой по gRPC через общий секрет, а с git-провайдером сервер общается через OAuth-приложение. Разберём на примере Gitea (для GitHub, GitLab процесс аналогичный, только переменные окружения называются WOODPECKER_GITHUB_* / WOODPECKER_GITLAB_*).
В Gitea зайдите в Settings → Applications → OAuth2 Applications и создайте приложение:
- Application Name:
Woodpecker CI - Redirect URI:
https://ci.example.com/authorize
Gitea выдаст Client ID и Client Secret — сохраните их, они понадобятся в переменных окружения.
Сгенерируйте общий секрет для связи server-agent:
openssl rand -hex 32
Создайте каталог проекта и docker-compose.yml:
mkdir -p /opt/woodpecker && cd /opt/woodpecker
services:
woodpecker-server:
image: woodpeckerci/woodpecker-server:latest
restart: unless-stopped
ports:
- "127.0.0.1:8000:8000"
volumes:
- woodpecker-server-data:/var/lib/woodpecker
environment:
- WOODPECKER_OPEN=false
- WOODPECKER_HOST=https://ci.example.com
- WOODPECKER_ADMIN=your_gitea_username
- WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
- WOODPECKER_GITEA=true
- WOODPECKER_GITEA_URL=https://git.example.com
- WOODPECKER_GITEA_CLIENT=${GITEA_CLIENT_ID}
- WOODPECKER_GITEA_SECRET=${GITEA_CLIENT_SECRET}
woodpecker-agent:
image: woodpeckerci/woodpecker-agent:latest
restart: unless-stopped
command: agent
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
- WOODPECKER_MAX_WORKFLOWS=2
volumes:
woodpecker-server-data:
Рядом положите .env с реальными значениями (в git его не кладите):
WOODPECKER_AGENT_SECRET=вставьте_сгенерированный_секрет
GITEA_CLIENT_ID=вставьте_client_id
GITEA_CLIENT_SECRET=вставьте_client_secret
WOODPECKER_ADMIN делает указанного пользователя администратором интерфейса — без этого после первого входа некому будет разрешать репозитории другим. WOODPECKER_OPEN=false запрещает регистрацию произвольных пользователей — оставляйте true только если Woodpecker закрыт за VPN и вы точно знаете, кто до него достучится.
Обратите внимание: агент монтирует /var/run/docker.sock — это значит, что контейнеры сборки получают доступ к Docker-демону хоста наравне с самим Woodpecker. Для чужого, недоверенного кода (публичные форки в PR) это небезопасно — там нужна отдельная изоляция (rootless-раннер или отдельная VM под сборки), но для внутренних приватных репозиториев такой setup — стандартная практика.
Поднимаем:
docker compose up -d
docker compose logs -f woodpecker-server
Reverse-proxy и HTTPS
Server слушает 127.0.0.1:8000 — наружу его напрямую не публикуем, ставим перед ним reverse-proxy с автоматическим SSL. Быстрее всего это делает Caddy — конфиг из статьи про Caddy с авто-SSL на VPS сокращённо выглядит так:
ci.example.com {
reverse_proxy 127.0.0.1:8000
}
Caddy сам выпустит и продлит сертификат Let's Encrypt при условии, что DNS-запись ci.example.com уже указывает на IP вашего VPS, а порты 80/443 открыты.
Если у вас уже стоит Traefik как единая точка входа для нескольких сервисов на сервере, добавьте лейблы к woodpecker-server в compose-файле вместо отдельного Caddy — логика та же, что для любого HTTP-сервиса за Traefik.
Проверьте, что порт 9000 (gRPC между server и agent) слушает только docker-compose внутри общей сети контейнеров и не проброшен наружу — в примере выше он вообще не вынесен в ports, что и требуется, если agent живёт на том же хосте. Если агент выносите на отдельный сервер, gRPC придётся публиковать и защищать TLS-сертификатом отдельно — для старта проще держать server и agent на одной машине.
Первый пайплайн: .woodpecker.yml
Зайдите на https://ci.example.com, авторизуйтесь через Gitea (или ваш провайдер), включите нужный репозиторий переключателем в списке — теперь на пуш в него будет приходить вебхук и создаваться пайплайн.
В корне репозитория создайте .woodpecker.yml:
steps:
install:
image: node:20
commands:
- npm ci
test:
image: node:20
commands:
- npm test
build:
image: node:20
commands:
- npm run build
when:
branch: main
deploy:
image: alpine:3
commands:
- echo "деплой на прод"
when:
branch: main
event: push
Каждый шаг — отдельный контейнер, они выполняются последовательно (если не указано иное через depends_on), а между шагами общее рабочее пространство — файлы, созданные на этапе install, доступны на test и build. Секция when фильтрует, при каких событиях и ветках шаг вообще запускается — это заменяет отдельные if из более сложных CI.
Секреты (токены, пароли) не пишите в YAML — добавляйте их через UI: Repository → Settings → Secrets, а в пайплайне обращайтесь как к переменной окружения:
deploy:
image: alpine:3
commands:
- echo "$DEPLOY_TOKEN" | some-deploy-tool
secrets: [ deploy_token ]
Коммитите .woodpecker.yml и делаете push — в веб-интерфейсе увидите живой лог сборки по шагам, с таймингами и кодами возврата каждой команды.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Чем Woodpecker принципиально лучше или хуже Drone CI?
Функционально почти идентичен последней открытой версии Drone, но развивается дальше и остаётся полностью бесплатным без ограничений по числу параллельных пайплайнов — то, что в текущем Drone доступно только в платном тарифе.
Можно ли использовать SQLite в проде?
Для одного-двух разработчиков и невысокой частоты сборок — да, это же значение по умолчанию. При росте команды и параллельных пайплайнов переходите на Postgres через WOODPECKER_DATABASE_DRIVER=postgres и WOODPECKER_DATABASE_DATASOURCE — под нагрузкой SQLite начинает блокироваться на конкурентной записи.
Нужен ли agent на отдельном сервере?
Нет, для старта агент прекрасно живёт в том же docker-compose, что и server, на одном VPS. Отдельные агенты имеет смысл добавлять, когда одного узла не хватает по CPU/RAM под параллельные сборки — тогда добавляете ещё один woodpecker-agent с тем же WOODPECKER_AGENT_SECRET на новом хосте.
Пайплайн падает с ошибкой доступа к Docker внутри шага сборки.
Обычная причина — вы монтируете сокет Docker в сам агент (это нужно для запуска шагов), но пытаетесь дополнительно собирать Docker-образы внутри шага командой docker build. Тогда монтируйте сокет ещё и в конкретный шаг явно через volumes в .woodpecker.yml, либо используйте образ docker:dind для изолированной сборки образов.
Как обновить Woodpecker до новой версии?
docker compose pull && docker compose up -d — при переходе через мажорные версии сверяйтесь с changelog проекта, между релизами иногда меняются имена переменных окружения и структура .woodpecker.yml.
Что делать, если вебхук от Gitea/GitHub не доходит до сервера?
Проверьте, что WOODPECKER_HOST в compose-файле совпадает с реальным внешним доменом (включая https://), а Redirect URI в OAuth-приложении провайдера — с WOODPECKER_HOST плюс /authorize. Расхождение в этих двух местах — самая частая причина, по которой авторизация или вебхуки молча не работают.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →