Integrating Drone CI with Gitea for self-hosted CI/CD is surprisingly straightforward, but the real magic lies in how Drone fundamentally rethinks the CI/CD pipeline structure.

Let’s see it in action with a basic .drone.yml for a Go project. Imagine this file lives in your Gitea repository:

kind: pipeline
type: docker
name: default

steps:
- name: build
  image: golang:1.18
  commands:
  - go build
  - go test

- name: push
  image: plugins/docker
  settings:
    repo: your-dockerhub-username/your-repo
    username:
      from_secret: docker_username
    password:
      from_secret: docker_password
  when:
    branch: main

When a commit is pushed to Gitea, Drone polls the repository. If it finds a .drone.yml file, it spins up a Docker container for each step defined. The build step uses a standard Go image to compile and test the code. The push step, however, uses the official Drone Docker plugin. It’s configured to push a Docker image to Docker Hub only when the commit is on the main branch. The username and password are pulled from Drone’s secret management, not hardcoded.

This setup solves the problem of needing complex build servers and intricate deployment scripts. Drone’s core concept is the declarative pipeline defined in .drone.yml. Instead of configuring jobs on a CI server, you define them in code, versioned alongside your application. The kind: pipeline and type: docker specify that this is a standard pipeline executed using Docker containers. Each step is an isolated container, ensuring a clean and reproducible build environment. The image directive for each step pulls a specific Docker image, guaranteeing that the build environment is consistent across all executions.

The settings block within a step allows you to configure plugins. The Docker plugin, for instance, can be instructed to push images, tag them, and even handle authentication. The from_secret mechanism is crucial for security, allowing sensitive information like API keys or passwords to be injected into the pipeline without being exposed in the repository.

The when clause provides powerful conditional execution. In our example, the push step only runs when the commit is on the main branch. This is a common pattern for deploying only production-ready code. You can define more complex conditions based on tags, commit messages, or even the status of previous steps.

Drone’s architecture relies on a central server and agents. The server handles webhook integrations, pipeline scheduling, and UI presentation, while the agents (also often running as Docker containers) execute the actual pipeline steps. When Drone polls Gitea, it receives information about the commit and the repository. It then interprets the .drone.yml and instructs an available agent to pull the specified Docker images and run the commands within them. This separation of concerns makes scaling easier; you can run multiple agents to handle a higher load.

What most people miss is how Drone’s pipeline definition implicitly handles dependencies between steps. By default, steps execute sequentially. If step-2 needs the output of step-1, you don’t explicitly define a dependency; you just ensure step-1 produces the artifact that step-2 needs. If step-1 fails, step-2 and subsequent steps are automatically skipped. This implicit ordering is a significant simplification over systems that require explicit DAG (Directed Acyclic Graph) definitions for every workflow.

The next hurdle you’ll likely encounter is managing more complex deployment strategies, like blue-green deployments or canary releases, using Drone’s plugin ecosystem.

Want structured learning?

Take the full Drone course →