Drone is a container-native CI/CD system: every pipeline step runs in its own Docker container, defined in a simple .drone.yml in your repository. It connects to GitHub, GitLab, Gitea, Gogs and Bitbucket, and runs comfortably on a small VPS. It’s a lightweight alternative to Jenkins or GitLab CI for self-hosters.
Licensing note: Drone is now owned by Harness. The open-source edition has limitations, and the community fork Woodpecker CI continues the original Apache-licensed codebase. For new setups, compare both. See Woodpecker CI.
Architecture
- Drone server: web UI, API, receives webhooks from your Git host, schedules builds.
- Runners: execute pipelines. The Docker runner runs each step in a container on its host.
Both can run on the same VPS, or put runners on separate build servers.
Requirements
- A VPS with Docker. 2 vCPU and 2–4 GB RAM is enough for small teams (builds need more). See install Docker.
- A domain (for example
ci.example.com) with HTTPS. - A Git host to connect: Gitea is a natural self-hosted pair. See Gitea.
1. Create an OAuth application
In Gitea: Settings → Applications → Create OAuth2 Application, with the redirect URI https://ci.example.com/login. Note the client ID and secret. (GitHub: Developer settings → OAuth Apps.)
2. Run server and runner
# /opt/drone/compose.yaml
services:
drone:
image: drone/drone:2
restart: unless-stopped
environment:
DRONE_GITEA_SERVER: https://git.example.com
DRONE_GITEA_CLIENT_ID: <client-id>
DRONE_GITEA_CLIENT_SECRET: <client-secret>
DRONE_RPC_SECRET: <openssl rand -hex 16>
DRONE_SERVER_HOST: ci.example.com
DRONE_SERVER_PROTO: https
DRONE_USER_CREATE: username:youradmin,admin:true
volumes:
- ./data:/data
ports:
- "127.0.0.1:8080:80"
runner:
image: drone/drone-runner-docker:1
restart: unless-stopped
environment:
DRONE_RPC_PROTO: http
DRONE_RPC_HOST: drone
DRONE_RPC_SECRET: <same secret>
DRONE_RUNNER_CAPACITY: 2
DRONE_RUNNER_NAME: runner-1
volumes:
- /var/run/docker.sock:/var/run/docker.sock
cd /opt/drone && docker compose up -d
Put Nginx with HTTPS in front of port 8080. See Nginx reverse proxy and Let’s Encrypt.
The runner mounts the Docker socket, so pipelines can effectively control the host. Run it on a dedicated build VPS, and only for repositories you trust.
3. Activate a repository and add a pipeline
Log in to https://ci.example.com, activate the repository, and commit .drone.yml:
kind: pipeline
type: docker
name: default
steps:
- name: test
image: node:22
commands:
- npm ci
- npm test
- name: build-and-push
image: plugins/docker
settings:
registry: registry.example.com
repo: registry.example.com/myapp
tags: [ "${DRONE_COMMIT_SHA:0:8}" ]
username: { from_secret: registry_user }
password: { from_secret: registry_password }
when:
branch: [ main ]
- name: deploy
image: appleboy/drone-ssh
settings:
host: { from_secret: vps_host }
username: deploy
key: { from_secret: vps_ssh_key }
script:
- cd /opt/app && docker compose pull && docker compose up -d
when:
branch: [ main ]
Add secrets in the repository settings in the Drone UI (or with the drone CLI). See Docker private registry.
Tips
- Use
when:conditions for branches, events (push, tag, pull_request) and paths. - Cache dependencies with volume or cache plugins to speed up builds.
- Keep runner hosts updated, and clean old images regularly (
docker system prunevia a cron job). See Docker disk cleanup.
Quick reference
| Item | Value |
|---|---|
| Server image | drone/drone:2 |
| Runner image | drone/drone-runner-docker:1 |
| Shared secret | DRONE_RPC_SECRET |
| Pipeline file | .drone.yml |
| Secrets | from_secret: |
| Open-source fork | Woodpecker CI |
Related: Gitea Actions and CI/CD pipeline explained.