Drone CI runners: Docker, exec and Kubernetes runners, routing and autoscaling
A Drone runner is a separate daemon that connects to your Drone server with a shared secret (DRONE_RPC_SECRET) and the server's address (DRONE_RPC_HOST), then executes pipelines. Most installs run the Docker runner in a container next to the Docker daemon; the exec runner covers jobs that need the bare host. Before you build a new fleet, check where the project stands: Harness now develops Drone's successor as Harness Open Source, and Woodpecker CI is the community fork.
Drone's project status in 2026
Harness acquired Drone in 2020. Its next step was Gitness, which added Git hosting to Drone's pipelines, and Harness has since renamed Gitness to Harness Open Source. The harness/harness repository describes it as the next generation of Drone, with source control, hosted developer environments and an artifact registry on top of CI. Harness keeps a snapshot of Drone on a drone branch of that repository so Drone development can continue, and says Harness Open Source aims for pipeline parity with Drone over time.
Licensing matters for runners. Harness ships the Drone server as the Enterprise Edition, which Drone's docs describe as free for organizations under US$1 million in annual gross revenue. The Open Source Edition, which you compile yourself with go build -tags "oss nolimit", lacks distributed runners, Kubernetes support, autoscaling and organization secrets. If you need the separate runners this guide covers, you need the Enterprise Edition and its license terms.
The alternative is Woodpecker CI, a community fork of Drone 0.8 that started in 2019 when Drone moved away from the Apache 2.0 license. Woodpecker remains under the Apache 2.0 license, keeps a similar pipeline format and has its own agent model. For a new self-hosted setup, compare both before you commit.
How Drone runners work
The Drone server handles webhooks, the UI and the build queue. Runners poll the server over RPC, claim pipelines that match their type, platform and labels, run the steps and stream logs back. Drone has one runner per execution model, and the pipeline's type: field picks it:
| Runner | Pipeline type | Executes steps |
|---|---|---|
| Docker runner | type: docker | Each step in its own container on one host |
| Exec runner | type: exec | On the host itself, no containers |
| Kubernetes runner | type: kubernetes | Each pipeline as a pod (Beta) |
Runners connect outbound to the server, so you can place them in private networks as long as they reach the server's address.
Prerequisites: server and RPC secret
Generate a shared secret and give the same value to the server and every runner:
openssl rand -hex 16
The server reads it from DRONE_RPC_SECRET, along with DRONE_SERVER_HOST (your public hostname) and DRONE_SERVER_PROTO (https behind a TLS proxy). A GitHub-backed server looks like this:
docker run \
--volume=/var/lib/drone:/data \
--env=DRONE_GITHUB_CLIENT_ID=your-id \
--env=DRONE_GITHUB_CLIENT_SECRET=your-oauth-secret \
--env=DRONE_RPC_SECRET=bea26a2221fd8090ea38720fc445eca6 \
--env=DRONE_SERVER_HOST=drone.example.com \
--env=DRONE_SERVER_PROTO=https \
--publish=80:80 \
--publish=443:443 \
--restart=always \
--detach=true \
--name=drone \
drone/drone:2
Swap the DRONE_GITHUB_* variables for the provider you use (GitLab, Gitea, Gogs, Bitbucket). The runner side needs three values: DRONE_RPC_PROTO, DRONE_RPC_HOST (the server hostname, with a port if it is not the default) and DRONE_RPC_SECRET.
Install the Docker runner
On a Linux host with Docker installed, start the runner with the Docker socket mounted. This follows the official Linux instructions:
docker run --detach \
--volume=/var/run/docker.sock:/var/run/docker.sock \
--env=DRONE_RPC_PROTO=https \
--env=DRONE_RPC_HOST=drone.example.com \
--env=DRONE_RPC_SECRET=bea26a2221fd8090ea38720fc445eca6 \
--env=DRONE_RUNNER_CAPACITY=2 \
--env=DRONE_RUNNER_NAME=build-01 \
--publish=3000:3000 \
--restart=always \
--name=runner \
drone/drone-runner-docker:1
Check the logs with docker logs runner. A healthy runner prints starting the server and successfully pinged the remote server. DRONE_RUNNER_CAPACITY sets how many pipelines the host runs at once. Size it to CPU and memory: two concurrent builds on a 4-vCPU host is a sane start.
A minimal pipeline for this runner, in .drone.yml:
kind: pipeline
type: docker
name: default
steps:
- name: test
image: golang:1.23
commands:
- go test ./...
Install the exec runner
The exec runner runs commands on the host without containers. Use it for macOS, Windows or Linux builds that need host tools, hardware or a GUI session. On Linux (amd64):
curl -L https://github.com/drone-runners/drone-runner-exec/releases/latest/download/drone_runner_exec_linux_amd64.tar.gz | tar zx
sudo install -t /usr/local/bin drone-runner-exec
Create /etc/drone-runner-exec/config (or ~/.drone-runner-exec/config for a non-root user):
DRONE_RPC_PROTO=https
DRONE_RPC_HOST=drone.example.com
DRONE_RPC_SECRET=bea26a2221fd8090ea38720fc445eca6
DRONE_LOG_FILE=/var/log/drone-runner-exec/log.txt
Install and start it as a service:
drone-runner-exec service install
drone-runner-exec service start
Look for successfully pinged the remote server in the log file. Pipelines target it with type: exec. Steps share the host's filesystem and user, so one build can see what the previous build left behind.
The Kubernetes runner
Drone's docs mark the Kubernetes runner as Beta: "may not be suitable for production workloads" and "a community effort and is not subject to support services or service level agreements." Installation uses a plain manifest with RBAC rules and a Deployment. If Kubernetes is your main target, weigh that status against Woodpecker's Kubernetes backend or the Docker runner on autoscaled VMs.
Route pipelines with platform and node
The platform section sets the target OS and architecture. Runners only accept pipelines for their own platform, so an arm64 runner picks up arm64 pipelines:
kind: pipeline
type: docker
name: arm
platform:
os: linux
arch: arm64
steps:
- name: build
image: alpine
commands:
- uname -m
Without a platform block, Drone assumes linux/amd64. For finer routing, give the runner labels with DRONE_RUNNER_LABELS:
--env=DRONE_RUNNER_LABELS=gpu:a10,zone:eu
Then match them with node in the pipeline:
node:
gpu: a10
zone: eu
The match must cover every label on the runner. A pipeline that sets only gpu: a10 does not reach a runner labelled gpu:a10,zone:eu. Keep label sets small and consistent across runners in the same pool.
Autoscaling with drone/autoscaler
The Drone autoscaler is a separate service (drone/autoscaler) that watches the server's queue and creates or destroys cloud VMs running the Docker runner. It has drivers for AWS, Google Cloud, DigitalOcean, OpenStack and other providers. Key settings:
DRONE_SERVER_PROTO,DRONE_SERVER_HOSTandDRONE_SERVER_TOKEN: where the server is and an admin token to read the queue.DRONE_AGENT_TOKEN: the shared RPC secret the new VMs use.DRONE_POOL_MINandDRONE_POOL_MAX: the floor and ceiling of servers (defaults 2 and 4).- Provider settings such as
DRONE_AMAZON_INSTANCE,DRONE_AMAZON_REGION,DRONE_AMAZON_SUBNET_IDandDRONE_AMAZON_SECURITY_GROUP.
The autoscaler scales whole VMs, each of which runs several pipelines up to its capacity. It does not give you one fresh VM per build. Set DRONE_POOL_MIN to 0 if you accept boot latency in exchange for zero idle cost.
Security hardening
- Treat the RPC secret like a root password. Anyone holding it can register a runner and receive your pipelines, including their secrets. Rotate it by changing the server and all runners together.
- The Docker socket is root. Mounting
/var/run/docker.sockgives the runner, and any privileged step, control of the host. Run Docker runners on dedicated VMs and keep them away from production networks. - Gate pull requests. Drone does not expose secrets to pull request builds unless you tick Allow Pull Requests on the secret. Leave it unticked. For public repositories, sign
.drone.ymlwithdrone signand enable the repository's Protected setting: a changed, unsigned pipeline file then waits for approval from someone with write access. - Avoid the exec runner for untrusted code. It has no isolation between builds or between the build and the host.
Troubleshooting
Runner logs show authentication or ping errors
DRONE_RPC_SECRET on the runner differs from the server, or DRONE_RPC_HOST includes a scheme. Set the host to a bare hostname (drone.example.com) and the scheme in DRONE_RPC_PROTO.
Builds stay pending
No runner matches the pipeline's type, platform or node. Check that a runner of that type shows as connected and that its labels match all of the pipeline's node entries.
Builds stay pending on the Open Source Edition
The OSS build lacks distributed runners. Use the Enterprise Edition or move to Woodpecker.
Disk full on Docker runner hosts
Images and volumes from old builds pile up. Schedule docker system prune or rebuild the hosts on a schedule.
Cost trade-offs
A single Docker runner host with capacity 2 to 4 handles a small team for the cost of one VM. Costs grow with idle capacity: a fleet sized for peak load sits empty overnight. The autoscaler trims that, at the price of boot delays and one more service to run and upgrade. Add the license question (Enterprise Edition terms above US$1 million revenue) and the maintenance outlook for Drone itself when you compare against Woodpecker or a hosted CI.
FAQ
Is Drone CI still maintained?
Harness maintains Drone's code on a drone branch of the harness/harness repository and focuses new development on Harness Open Source, the renamed Gitness. Check the repository's recent commits before you start a new deployment.
Can I run the Drone runner without Docker?
Yes. The exec runner runs pipelines on the host itself and installs as a system service on Linux, macOS and Windows.
Is Woodpecker CI compatible with Drone pipelines?
Woodpecker forked from Drone 0.8, so the YAML looks similar, but the formats have diverged. Expect to port .drone.yml files. The Woodpecker agents guide covers its setup.
Does Drone support ephemeral runners?
The Docker runner gives each step a fresh container, while the host persists. For a fresh VM per build, you need your own provisioning layer; the stock autoscaler reuses VMs across builds. Compare with Gitea's act_runner, which registers one-job runners with --ephemeral.
Drone and cirunner.dev
cirunner.dev provisions a fresh VM per job, registers it with your CI using a token you provide, scales with the queue and destroys the machine after the job. You choose CPU, RAM, x86_64 or arm64, region, base image and an optional GPU, and pay per compute minute.
The service is in early access with GitHub Actions and GitLab CI as launch platforms. Drone is on the roadmap; vote for it on the early-access form if you want per-build VMs for your Drone server.
Skip the runner fleet
cirunner.dev boots a fresh VM for every Drone job, registers it, and destroys it when the job ends. Drone is on our roadmap. Vote for it on the early-access form.
Join early access