You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
107 lines
4.8 KiB
Markdown
107 lines
4.8 KiB
Markdown
---
|
|
layout: post
|
|
title: "Migrating from Drone CI to Woodpecker CI"
|
|
date: 2026-04-18
|
|
tags: [ci, devops, drone, woodpecker, gitea, docker]
|
|
---
|
|
|
|
[Woodpecker CI](https://woodpecker-ci.org/) is a community fork of Drone CI, created when Drone changed its licensing model around 2021 and moved features behind a paid enterprise tier. Woodpecker continues as a fully open-source (Apache 2.0) project with active development, while Drone itself has been largely absorbed into Harness CI and is in maintenance mode.
|
|
|
|
If you're running Drone with Gitea, Woodpecker is a natural migration target — it has strong Gitea integration and an actively maintained codebase. Here is what the migration actually involves, based on a real setup using Docker Compose and Traefik.
|
|
|
|
## Docker Compose
|
|
|
|
The server and runner/agent images change, and there are several environment variable renames. Woodpecker also uses different ports: the web UI runs on **8000** (not 80), and the agent communicates with the server over gRPC on port **9000**.
|
|
|
|
```yaml
|
|
woodpecker:
|
|
image: woodpeckerci/woodpecker-server:latest
|
|
container_name: woodpecker
|
|
restart: unless-stopped
|
|
environment:
|
|
- WOODPECKER_HOST=https://drone.example.org # full URL including scheme
|
|
- WOODPECKER_GITEA=true
|
|
- WOODPECKER_GITEA_URL=https://gitea.example.org
|
|
- WOODPECKER_CRON_DISABLED=true
|
|
env_file:
|
|
- ./woodpecker.env
|
|
volumes:
|
|
- woodpecker:/var/lib/woodpecker # different path from Drone's /data
|
|
networks:
|
|
- services
|
|
- woodpecker
|
|
labels:
|
|
traefik.enable: "true"
|
|
traefik.http.services.woodpecker.loadbalancer.server.port: 8000 # not 80!
|
|
|
|
woodpecker-agent:
|
|
image: woodpeckerci/woodpecker-agent:latest
|
|
restart: unless-stopped
|
|
environment:
|
|
- WOODPECKER_SERVER=woodpecker:9000 # gRPC port, not HTTP
|
|
- WOODPECKER_GRPC_SECURE=false
|
|
env_file:
|
|
- ./woodpecker.env
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
networks:
|
|
- woodpecker
|
|
```
|
|
|
|
Make sure both the server and agent containers are on the **same Docker network**. If the agent can resolve the server's hostname but port 9000 is refused, it is likely a startup ordering issue — simply restarting the agent container after the server is fully up will fix it.
|
|
|
|
## Environment Variables
|
|
|
|
Your `drone.env` secrets need to be renamed in a new `woodpecker.env` file:
|
|
|
|
| `drone.env` | `woodpecker.env` |
|
|
|---|---|
|
|
| `DRONE_GITEA_CLIENT_ID` | `WOODPECKER_GITEA_CLIENT` |
|
|
| `DRONE_GITEA_CLIENT_SECRET` | `WOODPECKER_GITEA_SECRET` |
|
|
| `DRONE_RPC_SECRET` | `WOODPECKER_AGENT_SECRET` |
|
|
| `DRONE_COOKIE_SECRET` | `WOODPECKER_SECRET` |
|
|
|
|
Note that `WOODPECKER_AGENT_SECRET` (RPC) and `WOODPECKER_SECRET` (cookie signing) are **separate variables** — do not conflate them.
|
|
|
|
You also need to set `WOODPECKER_ADMIN` to your Gitea username, otherwise Woodpecker will not allow you to register or access the admin panel:
|
|
|
|
```
|
|
WOODPECKER_ADMIN=yourusername
|
|
```
|
|
|
|
You will also need to create a **new OAuth application in Gitea** pointing at your Woodpecker URL, since the callback URL changes.
|
|
|
|
## Pipeline Files
|
|
|
|
**You must rename `.drone.yml` to `.woodpecker.yml`.** Despite what the documentation suggests, Woodpecker (at least on the `next` tag) does not fall back to `.drone.yml` — it will silently ignore it, and pushes will not trigger any pipelines.
|
|
|
|
The pipeline syntax is largely compatible. Steps, images, commands, secrets, and `when` conditions mostly work as-is. A few things to check:
|
|
|
|
**Move `trigger` to the pipeline level.** In Drone it was sometimes written under individual steps; in Woodpecker it must be a top-level key.
|
|
|
|
|
|
**Drone plugins still work.** They are just Docker images, so plugins like `drillster/drone-rsync` are fully compatible.
|
|
|
|
**`DRONE_*` environment variables become `CI_*`.** If your pipeline scripts reference injected variables like `DRONE_COMMIT`, these are renamed in Woodpecker (e.g. `CI_COMMIT_SHA`).
|
|
|
|
## What Does Not Migrate
|
|
|
|
Woodpecker does not import Drone's database. This means:
|
|
|
|
- **Build history is lost** — you start with a clean slate
|
|
- **Secrets must be re-entered** via the Woodpecker UI for each repository
|
|
- **Repos must be re-activated** in Woodpecker
|
|
|
|
If retaining build history matters, you can keep the old Drone instance running read-only alongside Woodpecker temporarily, since they can coexist on different hostnames.
|
|
|
|
## Summary
|
|
|
|
For a straightforward Drone + Gitea + Docker Compose setup, the migration is low effort. The main steps are:
|
|
|
|
1. Update `docker-compose.yml` with the new images and port numbers
|
|
2. Rename secrets in your env file and set `WOODPECKER_ADMIN`
|
|
3. Create a new OAuth app in Gitea
|
|
4. Rename `.drone.yml` to `.woodpecker.yml` in each repository
|
|
5. Move any pipeline-level `trigger` blocks to the top level
|
|
6. Re-activate repos and re-enter secrets in the Woodpecker UI
|