Drone’s pipeline configuration feels like writing a small program, but instead of executing arbitrary code, you’re orchestrating builds, tests, and deployments.
Let’s see a simple pipeline in action. Imagine you want to build a Docker image and push it to a registry whenever you push changes to your main branch.
kind: pipeline
type: docker
name: default
steps:
- name: build_and_push
image: plugins/docker
settings:
repo: your-dockerhub-username/your-repo-name
tags:
- latest
- ${DRONE_COMMIT_SHA:0:8}
when:
branch: main
trigger:
branch:
include:
- main
Here’s what’s happening:
kind: pipelineandtype: docker: These tell Drone this is a pipeline definition and that it will run on a Docker execution environment.name: default: This is just a friendly name for the pipeline.steps:: This is where the magic happens. Each item in this list is a container that Drone will spin up and execute your commands within.- name: build_and_push: A descriptive name for this step.image: plugins/docker: This is the Docker image Drone will use for this step. Drone has a rich ecosystem of official plugins for common tasks like Docker, Git, Slack, etc. This specific image is the official Docker plugin.settings:: These are arguments passed to theplugins/dockerimage.repo: your-dockerhub-username/your-repo-name: This tells the Docker plugin which repository to push the image to. You’d replaceyour-dockerhub-usernameandyour-repo-namewith your actual Docker Hub credentials and repository name.tags:: Here we’re defining tags for the Docker image.latestis standard, and${DRONE_COMMIT_SHA:0:8}is a Drone-specific variable that inserts the first 8 characters of the current commit SHA, providing a unique tag for each build.
when:: This is a conditional. This step only runs if thebranchismain.
trigger:: This section defines when the entire pipeline should be activated.branch:: We’re specifying that the pipeline should trigger.include: - main: This means the pipeline will only run for pushes to themainbranch.
The core problem Drone solves is making Continuous Integration and Continuous Deployment repeatable, observable, and automated. Instead of manually cloning repos, building images, running tests, and deploying, you declare it once in a .drone.yml file in your repository. Drone then watches your Git repository for triggers (like pushes or pull requests) and executes the defined steps in isolated Docker containers.
Internally, Drone orchestrates these Docker containers. When a pipeline is triggered, Drone’s server (the control plane) communicates with its agents (the execution plane, often running on Docker or Kubernetes). The agent pulls the specified Docker images for each step, mounts your repository’s code into the container, and runs the commands you’ve defined. Services, which we haven’t shown here but are crucial for things like databases during testing, are also spun up as separate containers that are network-accessible to your build steps.
The settings block within a step is not just for plugins. If you were running a custom script, you could have:
steps:
- name: run_tests
image: node:18
commands:
- npm install
- npm test
Here, image: node:18 specifies the base image, and commands: lists the shell commands to execute sequentially within that container. Drone handles the npm install and npm test execution, and if any command exits with a non-zero status, the pipeline step fails.
The secrets directive is how you securely inject sensitive information into your pipeline. Instead of hardcoding credentials, you define them in Drone’s UI or via the CLI, and then reference them in your .drone.yml:
steps:
- name: deploy
image: alpine
commands:
- echo "Deploying to production with API key: ${MY_API_KEY}"
environment:
MY_API_KEY:
from_secret: api_key
When this pipeline runs, Drone fetches the value of the api_key secret from its secure storage and makes it available as the MY_API_KEY environment variable only to that specific step. This prevents sensitive data from appearing in logs or your configuration files.
A common point of confusion is the difference between trigger and when. The trigger block defines when the pipeline as a whole should start. The when block within a step defines conditions under which that specific step will execute if the pipeline has already started. You can have a pipeline that triggers on any push, but only certain steps run on specific branches or tags.
You can also define services that run alongside your build steps, providing dependencies like databases or caches. These services are accessible via standard network protocols from your build steps. For example, to run tests against a PostgreSQL database:
services:
- name: postgres
image: postgres:14
environment:
POSTGRES_USER: test
POSTGRES_DB: testdb
steps:
- name: test
image: node:18
commands:
- npm install
- npm test
environment:
DATABASE_HOST: postgres # Service name is resolvable as hostname
DATABASE_NAME: testdb
DATABASE_USER: test
Here, the postgres service is started in its own container before the test step begins. The test step can then connect to postgres on port 5432 (the default for PostgreSQL) using postgres as the hostname.
The most surprising thing about Drone’s configuration is how deeply it mirrors Docker’s own structure, making it feel very natural if you’re already familiar with containers. The entire pipeline, all its steps, services, and dependencies, are defined as Docker images and their configurations, allowing Drone to treat your build environment as just another set of containers to manage.
The next concept to explore is how to orchestrate multiple pipelines and manage complex workflows using pipeline dependencies and multi-stage builds.