为什么选 Woodpecker CI
#如果你已经在用 Gitea 自建 Git 服务,那下一步自然会需要 CI/CD。选项不少:
- Gitea Actions:内置,语法兼容 GitHub Actions,生态丰富,但 Runner 相对重,配置稍复杂
- Jenkins:功能强大,但资源占用高,配置复杂,界面老旧
- Woodpecker CI:从 Drone CI fork 而来,Apache 2.0 开源,与 Gitea 原生集成,Server + Agent 合计空闲内存不到 200MB,YAML 配置简单直观
对个人或小团队自建场景,Woodpecker 是性价比最高的选择。它目前是 Codeberg(隐私友好的 GitHub 替代品)的主力 CI/CD 引擎,社区活跃,2026 年仍在积极维护,最新稳定版 v3.13.0。
架构概览
#Woodpecker 由两个组件构成:
graph LR
Gitea["Gitea
代码仓库"] -->|webhook| Server["Woodpecker Server
:8000 管理界面
:9000 Agent 通信"]
Server -->|任务分发| Agent["Woodpecker Agent"]
Agent -->|启动临时容器| Container["Docker 容器
执行 Pipeline 步骤"]
Container -->|执行完毕| Destroy["容器销毁
环境干净"]
Server:调度中心,提供 Web 管理界面,接收 Gitea webhook,分发任务。
Agent:执行者,主动连接 Server(单向),拉取任务,在 Docker 容器里执行 pipeline 步骤,执行完容器自动销毁。
关键点:Agent 主动连接 Server,不是反过来。这意味着远程 Agent 不需要开放任何入站端口,只要能访问 Server 的 9000 端口即可,部署极其灵活。
部署前准备
#1. Gitea 创建 OAuth 应用
#登录 Gitea → 右上角头像 → Settings → Applications → Manage OAuth2 Applications
填写:
- Application Name:Woodpecker
- Redirect URI:
https://ci.yourdomain.com/authorize
创建后保存好 Client ID 和 Client Secret,只显示一次。
2. 允许本地 Webhook(同机部署必须)
#如果 Gitea 和 Woodpecker 在同一台机器,需要在 Gitea 配置文件(通常是 /etc/gitea/conf/app.ini 或 Docker volume 内)添加:
1
2
| [webhook]
ALLOWED_HOST_LIST=*
|
否则 Gitea 会拒绝向本机地址发送 webhook,pipeline 永远无法触发。
3. 生成 Agent Secret
#1
2
| openssl rand -hex 32
# 或者任意的复杂长度复杂token
|
Server 和 Agent 共用这个值做身份验证,妥善保存。
部署:两个独立的 Docker Compose
#推荐 Server 和 Agent 分开放在不同目录,职责清晰,独立升级。同时版本号最好一致,避免出现问题。
Server:/opt/woodpecker-server/
#.env 文件(敏感信息单独存放,不进 compose 文件):
1
2
3
4
5
6
| WOODPECKER_HOST=https://ci.yourdomain.com
WOODPECKER_GITEA_URL=https://git.yourdomain.com
WOODPECKER_GITEA_CLIENT=你的ClientID
WOODPECKER_GITEA_SECRET=你的ClientSecret
WOODPECKER_AGENT_SECRET=上面生成的随机字符串
WOODPECKER_ADMIN=你的Gitea用户名
|
docker-compose.yml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| services:
woodpecker-server:
image: woodpeckerci/woodpecker-server:v3.13.0
container_name: woodpecker-server
restart: unless-stopped
ports:
- "127.0.0.1:8000:8000" # 只本机反向代理访问
- "10.0.4.39:9000:9000" # 只内网 Agent 访问,如果开放公网访问,最好配置防火墙只允许Agent客户端访问
volumes:
- woodpecker-server-data:/var/lib/woodpecker/
environment:
- WOODPECKER_OPEN=false
- WOODPECKER_HOST=${WOODPECKER_HOST}
- WOODPECKER_GITEA=true
- WOODPECKER_GITEA_URL=${WOODPECKER_GITEA_URL}
- WOODPECKER_GITEA_CLIENT=${WOODPECKER_GITEA_CLIENT}
- WOODPECKER_GITEA_SECRET=${WOODPECKER_GITEA_SECRET}
- WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
- WOODPECKER_ADMIN=${WOODPECKER_ADMIN}
networks:
- 1panel-network
volumes:
woodpecker-server-data:
networks:
1panel-network:
external: true
|
Agent:/opt/woodpecker-agent/
#.env 文件:
1
2
| WOODPECKER_SERVER=woodpecker-server:9000
WOODPECKER_AGENT_SECRET=和Server一样的值
|
docker-compose.yml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| services:
woodpecker-agent:
image: woodpeckerci/woodpecker-agent:v3.13.0
container_name: woodpecker-agent
hostname: main-agent # Agent 显示名称
restart: unless-stopped
volumes:
- woodpecker-agent-config:/etc/woodpecker
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WOODPECKER_SERVER=${WOODPECKER_SERVER}
- WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
- WOODPECKER_MAX_WORKFLOWS=2
- WOODPECKER_LABELS=location=main
networks:
- 1panel-network
volumes:
woodpecker-agent-config:
networks:
1panel-network:
external: true
|
启动顺序
#1
2
3
4
5
6
7
8
9
| # 先启动 Server
cd /opt/woodpecker-server && docker compose up -d
# 再启动 Agent
cd /opt/woodpecker-agent && docker compose up -d
# 检查日志
docker logs -f woodpecker-server
docker logs -f woodpecker-agent
|
反向代理配置
#管理界面需要通过反向代理对外提供 HTTPS 访问。
Caddy(推荐,自动证书):
1
2
3
| ci.yourdomain.com {
reverse_proxy localhost:8000
}
|
Nginx:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| server {
listen 443 ssl;
server_name ci.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/ci.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ci.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
|
激活仓库
#这一步很多人会忽略,导致 Gitea 里看不到 webhook。
Webhook 不是自动注册的,必须在 Woodpecker 里手动激活仓库:
- 登录
https://ci.yourdomain.com,用 Gitea OAuth 授权 - 点击左上角 Repositories 或 +
- 找到目标仓库,点右边的开关激活
激活后 Woodpecker 才会去 Gitea 注册 webhook。去 Gitea 仓库 → Settings → Webhooks 验证是否成功注册。
第一条 Pipeline
#在仓库根目录创建 .woodpecker.yml,推送后自动触发:
1
2
3
4
5
6
| steps:
- name: hello
image: alpine
commands:
- echo "Woodpecker is working!"
- date
|
推送后在 Woodpecker 管理界面能看到 pipeline 运行状态和日志。
关键坑总结
#坑 1:latest tag 已废弃
#官方已明确废弃 latest tag,用 v3.13.0 这样的精确版本。
更重要的是:Server 和 Agent 版本必须完全一致,版本不匹配会导致 gRPC 协议不兼容,Agent 无法连接 Server,报错类似 grpc version mismatch。
坑 2:WOODPECKER_HOST 必须填对
#这个值用于两个关键用途:向 Gitea 注册的 webhook 回调地址,以及 OAuth 登录的回调地址。
填错或填内网地址会导致 webhook 打不到 Woodpecker,pipeline 永远不会触发。必须是公网可访问的完整地址,格式 https://ci.yourdomain.com,结尾不加斜杠。
坑 3:8000 和 9000 端口的暴露策略
#- 8000(管理界面):有反向代理的情况下绑定
127.0.0.1:8000,不对外暴露,走反向代理的 443 - 9000(Agent gRPC):只绑定内网 IP,绝对不要直接对公网暴露,Agent 通过内网或 VPN 连接
坑 4:Gitea 和 Woodpecker 同机的网络问题
#Agent clone 代码时用的是 Gitea API 返回的 URL,如果两者都在 Docker 里,Agent 容器必须加入 Gitea 所在的 Docker 网络,否则 clone 步骤会失败。
查看 Gitea 容器所在网络:
1
| docker inspect gitea --format '{{ "{{" }}range $k,$v := .NetworkSettings.Networks{{ "}}" }}{{ "{{" }}$k{{ "}}" }}{{ "{{" }}end{{ "}}" }}'
|
然后在 Agent 的 compose 里加入这个网络,或设置:
1
2
| environment:
- WOODPECKER_BACKEND_DOCKER_NETWORK=gitea的网络名
|
坑 5:Cloudflare 不兼容 gRPC 长连接
#如果域名走了 Cloudflare 橙云代理,Agent 的 gRPC 连接会超时断开。
解决方案:Agent 通信域名在 Cloudflare 设为 DNS Only(灰云),或让 Agent 直接连内网 IP,管理界面可以继续走 Cloudflare。
远程 Agent 部署
#得益于 Agent 主动连接的设计,在任意远程服务器部署 Agent 非常简单。远程 Agent 需要访问 Server 的 9000 端口,推荐通过 WireGuard 等 VPN 组网连接内网 IP;如果必须走公网,务必用防火墙白名单限制来源 IP(参考上面坑 3)。
1
2
3
4
5
6
7
8
9
| docker run -d \
--name woodpecker-agent \
--restart unless-stopped \
-e WOODPECKER_SERVER=ci.yourdomain.com:9000 \
-e WOODPECKER_AGENT_SECRET=你的secret \
-e WOODPECKER_HOSTNAME=agent-hongkong \
-e WOODPECKER_LABELS="location=hk" \
-v /var/run/docker.sock:/var/run/docker.sock \
woodpeckerci/woodpecker-agent:v3.13.0
|
在 pipeline 里用 label 指定在哪台 Agent 上执行:
1
2
3
4
5
6
7
8
| labels:
location: hk
steps:
- name: deploy
image: alpine
commands:
- ./deploy.sh
|
集成 Trivy 安全扫描
#在 pipeline 里直接加入 Trivy,推送自动扫描,PR 时严格卡漏洞:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| steps:
# 日常 push:扫描但不阻断,只看日志
- name: trivy-scan
image: aquasec/trivy
commands:
- trivy fs --severity HIGH,CRITICAL --exit-code 0 .
when:
event: push
# PR 时:发现高危直接失败,阻止合并
- name: trivy-strict
image: aquasec/trivy
commands:
- trivy fs --severity HIGH,CRITICAL --exit-code 1 .
when:
event: pull_request
|
无需额外部署任何服务,直接跑容器,扫完自动销毁。
Woodpecker CI + Gitea 是目前自建 CI/CD 最轻量、最易维护的组合之一。整套系统空闲资源占用不到 300MB,零外部依赖,配置跟随代码走,数据完全在自己手里。
官方文档覆盖了所有配置项,但这篇文章希望补充官方文档没有强调的决策原因和实际踩坑。如果你按照这篇文章部署遇到问题,大概率是以下几个地方之一:Gitea webhook 本地访问限制、网络不通、版本不一致、WOODPECKER_HOST 填错。