← Back to DevBytes

Drone CI: Complete Configuration Guide

Understanding Drone CI Configuration

Drone CI is a container-native continuous integration platform built on Docker. Unlike traditional CI tools that rely on agent-based architectures, Drone executes every build step inside isolated containers. This approach guarantees reproducible builds regardless of the host environment. The configuration for a Drone pipeline lives entirely in a single YAML file stored alongside your source code — typically named .drone.yml — making pipelines version-controlled, auditable, and portable.

When you push code to your repository, Drone reads this configuration file, pulls the specified container images, and executes your pipeline steps in the exact order you define. There are no hidden plugins, no opaque server-side configurations, and no drift between local and CI environments. Everything is explicit, transparent, and self-documenting.

Core Architecture Concepts

Before diving into syntax, it's important to understand Drone's execution model. A Drone pipeline consists of steps that run in sequence by default. Each step operates inside its own container. Steps can optionally specify depends_on to run in parallel. Pipelines can also declare services — auxiliary containers that start alongside steps and provide supporting infrastructure like databases or caches during the build. The entire pipeline is triggered by events such as pushes, pull requests, or tags.

Basic Pipeline Structure

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

The simplest possible Drone configuration looks like this:

kind: pipeline
type: docker
name: default

steps:
- name: greet
  image: alpine:3.18
  commands:
  - echo "Hello from Drone CI"
  - echo "Build started at $(date)"

Let's break down each top-level field:

Step Configuration in Detail

A step has several configurable properties beyond just name and image. Here is a fully-annotated step block:

- name: build
  image: golang:1.21
  commands:
  - go build -o app ./cmd/server
  - go vet ./...
  environment:
    GOOS: linux
    GOARCH: amd64
    CGO_ENABLED: "0"
  volumes:
  - name: deps
    path: /go/pkg/mod
  depends_on:
  - clone
  when:
    branch: main
    event: push

Key step-level fields explained:

Using Plugins for Common Tasks

Drone's plugin ecosystem is one of its greatest strengths. A plugin is simply a Docker container designed to accept environment variables as parameters and perform a specific task — publishing artifacts, sending notifications, deploying to cloud providers, etc. Plugins are invoked by setting the step's image to the plugin's Docker image and providing settings under a dedicated settings block:

- name: publish
  image: plugins/docker:linux-amd64
  settings:
    registry: registry.example.com
    repo: registry.example.com/my-app
    username:
      from_secret: docker_username
    password:
      from_secret: docker_password
    tags:
    - latest
    - ${DRONE_COMMIT_SHA}
  depends_on:
  - build

Notice how credentials are pulled from secrets using the from_secret directive. This avoids ever hardcoding sensitive values in the configuration file. The plugin receives these values as environment variables internally.

Services and Dependencies

Services are containers that run for the duration of the pipeline and are typically used for databases, caches, or mock APIs needed during testing. Unlike steps, services are not expected to exit — they run continuously until the pipeline completes:

services:
- name: postgres
  image: postgres:15-alpine
  environment:
    POSTGRES_USER: drone
    POSTGRES_PASSWORD:
      from_secret: pg_password
    POSTGRES_DB: drone_test

steps:
- name: test
  image: myapp:dev
  commands:
  - sleep 5  # wait for postgres to be ready
  - go test ./... -v
  environment:
    DATABASE_URL: postgres://drone:${PG_PASSWORD}@postgres:5432/drone_test?sslmode=disable

Drone automatically creates a Docker network for each pipeline. All steps and services can reach each other by their name as a hostname. In the example above, the test step connects to the Postgres service using postgres as the hostname. No port mapping or link configuration is required — container name resolution is built in.

Parallel Pipelines and Fan-Out Patterns

A single .drone.yml file can define multiple independent pipelines by listing them sequentially. Each pipeline has its own execution graph and can run in parallel with others:

---
kind: pipeline
type: docker
name: backend

steps:
- name: test
  image: golang:1.21
  commands:
  - go test ./...

---
kind: pipeline
type: docker
name: frontend

steps:
- name: lint
  image: node:20-alpine
  commands:
  - npm install
  - npm run lint
---
kind: pipeline
type: docker
name: deploy
depends_on:
- backend
- frontend

steps:
- name: release
  image: plugins/heroku
  settings:
    api_key:
      from_secret: heroku_key
    app_name: my-app

This configuration defines three pipelines. The backend and frontend pipelines run concurrently. The deploy pipeline uses the depends_on key at the pipeline level to wait for both to succeed before executing. This is a classic fan-in pattern that enables fast feedback while maintaining deployment safety.

Triggers and Conditional Execution

Drone provides granular control over when pipelines and steps execute. Conditions can filter on branch names, event types, commit messages, and even file changes:

steps:
- name: deploy-prod
  image: plugins/ssh
  settings:
    host: prod.example.com
    user: deploy
    script:
    - docker pull myapp:latest
    - docker compose up -d
  when:
    branch: main
    event: push
    status: success

- name: notify
  image: plugins/slack
  settings:
    webhook:
      from_secret: slack_webhook_url
    channel: deployments
  when:
    branch:
      include:
      - main
      - release/*
      exclude:
      - release/experimental-*

trigger:
  branch:
  - main
  - feature/*
  event:
  - push
  - pull_request
  ref:
  - refs/tags/v*
  action:
  - opened
  - synchronize

The when block on a step supports include and exclude patterns with glob-style matching. The pipeline-level trigger block determines whether the entire pipeline runs at all. For pull requests, the action field lets you specify whether the pipeline runs on opened, synchronize (new commits pushed to the PR), or merged events.

Environment Variables and Secrets Management

Drone injects a rich set of built-in environment variables into every step. These provide metadata about the commit, branch, tag, and build environment:

DRONE_BUILD_NUMBER=42
DRONE_COMMIT_SHA=a1b2c3d4e5f6...
DRONE_COMMIT_BRANCH=feature/new-api
DRONE_COMMIT_AUTHOR=Jane Doe
DRONE_COMMIT_AUTHOR_EMAIL=jane@example.com
DRONE_TAG=v2.1.0
DRONE_REPO=octocat/hello-world
DRONE_DEPTH=50

Secrets are managed separately from the configuration file. You add them via the Drone CLI or API, and they are injected into steps using from_secret:

environment:
  AWS_ACCESS_KEY_ID:
    from_secret: aws_access_key
  AWS_SECRET_ACCESS_KEY:
    from_secret: aws_secret_key

Secrets are encrypted at rest and are never exposed in build logs. Drone automatically masks secret values in the output. For pull request events, secrets are not available by default — you must explicitly grant access per-repository in the Drone settings to prevent secrets leaking through unverified PRs.

Working with Volumes and Workspaces

By default, each step starts with a fresh workspace containing only the cloned repository. To share data between steps, you have several options:

volumes:
- name: cache
  host:
    path: /var/drone/cache/${DRONE_REPO}
- name: artifacts
  temp: {}

steps:
- name: build
  image: node:20
  commands:
  - npm ci
  - npm run build
  - cp -r dist /drone/artifacts/

- name: package
  image: alpine:3.18
  commands:
  - tar czf release.tar.gz /drone/artifacts/
  volumes:
  - name: artifacts
    path: /drone/artifacts

- name: cache-deps
  image: node:20
  commands:
  - npm ci --cache /drone/cache
  volumes:
  - name: cache
    path: /drone/cache

The built-in drone workspace is automatically mounted at /drone/src in every step. You can customize this with the workspace block at the pipeline level if your project layout requires a different working directory.

Kubernetes Runner Configuration

When using the Kubernetes runner, the configuration syntax adapts slightly to leverage Kubernetes-native features. Instead of Docker volumes, you can mount ConfigMaps and Secrets directly. Resource limits become available:

kind: pipeline
type: kubernetes
name: k8s-pipeline

workspace:
  path: /go/src/github.com/octocat/hello-world

steps:
- name: build
  image: golang:1.21
  commands:
  - go build -o app .
  resources:
    requests:
      cpu: 500m
      memory: 256Mi
    limits:
      cpu: 2000m
      memory: 1Gi
  tolerations:
  - key: "dedicated"
    operator: "Equal"
    value: "ci"
    effect: "NoSchedule"

- name: deploy
  image: alpine/k8s:1.28
  commands:
  - kubectl apply -f deployment.yaml
  env_file:
  - name: kubeconfig
    secret_key: kubeconfig_file

The Kubernetes runner gives you access to resources for CPU and memory constraints, tolerations for node scheduling, and env_file for mounting entire secret files as environment variable sources. The underlying mechanism creates a Pod per pipeline step, ensuring isolation through the Kubernetes scheduler.

Matrix Builds

Drone supports matrix builds natively — no YAML templating or external tools required. Define a matrix block with variable dimensions, and Drone expands the pipeline automatically:

kind: pipeline
type: docker
name: test-matrix

steps:
- name: test
  image: ${IMAGE}
  commands:
  - echo "Testing on ${OS} with Go version ${GO_VERSION}"
  - go version
  - go test ./...
  environment:
    OS: ${OS}
    GO_VERSION: ${GO_VERSION}

matrix:
  GO_VERSION:
  - "1.20"
  - "1.21"
  - "1.22"
  OS:
  - linux
  - darwin
  IMAGE:
  - golang:${GO_VERSION}-${OS}

Drone computes the Cartesian product of all matrix dimensions. For the example above, that yields 3 × 2 = 6 distinct build combinations, each running in parallel. Matrix variables are available via ${VARIABLE} interpolation throughout the pipeline definition. You can combine matrix builds with exclude blocks to skip invalid combinations:

matrix:
  GO_VERSION:
  - "1.20"
  - "1.21"
  OS:
  - linux
  - darwin
  exclude:
  - GO_VERSION: "1.20"
    OS: darwin

Advanced Step Features

Custom Entrypoints and Shells

When you need Bash specifically or a custom entrypoint, override the container defaults:

- name: complex-setup
  image: alpine:3.18
  entrypoint:
  - /bin/bash
  - -c
  commands:
  - |
    set -euo pipefail
    for dir in src lib test; do
      echo "Processing $dir"
      ls -la "$dir"
    done

Failure Handling

Steps can declare failure strategies. The failure block lets a step execute specifically when previous steps fail — useful for cleanup or notification:

- name: cleanup
  image: alpine:3.18
  commands:
  - rm -rf /tmp/build-artifacts
  when:
    status: failure

- name: always-notify
  image: plugins/slack
  settings:
    webhook:
      from_secret: slack_webhook_url
  when:
    status:
    - success
    - failure

Clone Configuration

Drone automatically clones your repository before the first step runs. You can customize clone behavior:

clone:
  depth: 50
  tags: true
  strategy:
    method: merge
    ref: refs/heads/main
  skip_verify: false
  lfs: true
  submodule:
    override: true
    recursive: true
    include:
    - lib/common
    - lib/shared

Secrets at the Pipeline Level

Beyond step-level secrets, you can inject secrets into the entire pipeline. This is useful for credentials needed across multiple steps:

kind: pipeline
type: docker
name: production-deploy

environment:
  DOCKER_REGISTRY:
    from_secret: registry_url
  NPM_TOKEN:
    from_secret: npm_token

steps:
- name: docker-build
  image: plugins/docker
  settings:
    registry: ${DOCKER_REGISTRY}
    username:
      from_secret: docker_user
    password:
      from_secret: docker_pass

- name: npm-publish
  image: node:20
  commands:
  - echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
  - npm publish

Pipeline-level environment variables are inherited by all steps. This reduces repetition but remember that each variable is visible in every step's environment — be mindful of the principle of least privilege.

Best Practices for Drone CI Configuration

1. Keep Pipelines Deterministic

Pin container image tags to specific versions rather than using latest. A pipeline that worked yesterday should work identically today. Use digest pinning for critical production pipelines:

image: golang:1.21.6@sha256:abc123...

2. Structure Multi-Pipeline Repositories Clearly

For monorepos with multiple services, create one pipeline per service. Use pipeline-level depends_on to model the dependency graph rather than cramming everything into a single linear pipeline. This enables parallel execution and faster feedback loops.

3. Use Temporary Volumes for Artifact Passing

Avoid writing artifacts to the workspace directory unless you explicitly need them across steps. Temporary volumes are cleaner, isolated, and automatically cleaned up. Host volumes should be reserved for true persistent caching needs.

4. Leverage Built-in Environment Variables

Use DRONE_COMMIT_SHA, DRONE_BUILD_NUMBER, and DRONE_TAG for versioning artifacts rather than computing versions manually. These are guaranteed consistent across the entire pipeline execution.

5. Protect Secrets Aggressively

Never echo secrets in commands. Enable the skip_verify flag on clone only if you fully trust the TLS infrastructure. For pull requests from forks, ensure secret access is restricted — Drone's default behavior of blocking secrets on PRs is a security feature, not a limitation.

6. Use Matrix Builds Instead of Scripting Loops

When testing across multiple versions or platforms, use Drone's native matrix support rather than writing shell loops. Matrix builds are parallelized automatically and produce distinct, individually-rerunnable pipeline runs in the UI.

7. Version Control Your Pipeline Configuration

The .drone.yml file lives in your repository for a reason. Treat it like any other source file: review changes in pull requests, test pipeline modifications in feature branches, and roll back faulty configurations via Git revert. Never rely on UI-based pipeline configuration.

8. Prefer Plugins Over Custom Scripts

Before writing a complex shell script to deploy to AWS, publish to NPM, or notify Slack, check the Drone plugin registry. Plugins are maintained, tested, and handle edge cases like retries and error formatting. Custom scripts should be the exception, not the rule.

9. Implement Pipeline Notifications Early

Add a notification step — Slack, email, or webhook — as the first step after getting a green build. Notifications should include the commit SHA, branch, build number, and a direct link to the build log. This pays dividends when debugging failures later.

10. Use YAML Anchors for Repeated Configurations

When multiple steps share common configuration, use YAML anchors and aliases to reduce duplication:

definitions:
  common-env: &common-env
    environment:
      NODE_ENV: test
      CI: "true"

steps:
- name: lint
  <<: *common-env
  image: node:20
  commands:
  - npm run lint

- name: test
  <<: *common-env
  image: node:20
  commands:
  - npm test

Debugging and Troubleshooting Configuration

When a pipeline fails, Drone provides several mechanisms to diagnose issues:

drone lint --repo octocat/hello-world --path .drone.yml

The drone exec command lets you run pipelines locally for rapid iteration:

drone exec --pipeline default --include build --env-file .env.local

Local execution requires Docker running on your machine. It respects the pipeline configuration but skips triggers and some server-side features like secrets (you provide them via the env-file).

Conclusion

Drone CI's configuration model is intentionally simple: one YAML file, container-based steps, explicit dependencies, and declarative conditions. This simplicity scales remarkably well — from single-service hobby projects to monorepos with dozens of interconnected pipelines. By treating your CI configuration as source code, leveraging plugins for common tasks, protecting secrets rigorously, and using matrix builds for cross-platform testing, you build a CI system that is fast, reproducible, and maintainable. The skills you develop writing Drone pipelines translate directly to any container-based CI platform, but Drone's commitment to a single configuration file and native container execution keeps the experience focused and predictable. Start with a single pipeline, add services as needed, split into parallel pipelines when your project grows, and always validate with drone lint before you push.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles