docker.recipes

Drone CI with Gitea

advanced

Container-native CI/CD with Drone, Gitea integration, Docker runner, and secrets management.

Overview

Drone is a container-native continuous integration platform built on Docker that transforms Git commits into automated testing and deployment pipelines. Originally created by Brad Rydzewski in 2014, Drone has evolved into a powerful CI/CD system that executes pipeline steps inside ephemeral Docker containers, providing true isolation and reproducibility. Unlike traditional CI systems that require agents with pre-installed tools, Drone's container-first approach means every build runs in a clean environment with exactly the dependencies specified in your pipeline configuration. This stack combines Drone with Gitea, a lightweight self-hosted Git service that provides GitHub-like functionality without the resource overhead of GitLab or enterprise solutions. Together, they create a complete DevOps platform where code repositories, issue tracking, and automated pipelines operate within your own infrastructure. The integration between Gitea and Drone enables automatic webhook-triggered builds, pull request validation, and deployment automation while maintaining complete control over your source code and build artifacts. This combination is particularly valuable for organizations requiring air-gapped development environments, teams with strict data sovereignty requirements, or developers who want enterprise-grade CI/CD capabilities without recurring SaaS costs.

Key Features

  • Container-native pipeline execution with Docker-in-Docker support for isolated build environments
  • Native Gitea integration with OAuth2 authentication and automatic webhook configuration
  • YAML-based pipeline definitions with multi-stage builds, parallel execution, and conditional steps
  • Docker registry integration for automated image building, tagging, and deployment
  • Encrypted secrets management with per-repository and organization-level secret storage
  • Real-time build logs with terminal output streaming and downloadable artifacts
  • Pull request automation with status checks, approval workflows, and merge conflict detection
  • Multi-architecture build support for ARM, AMD64, and custom runner configurations

Common Use Cases

  • 1Self-hosted development teams requiring private Git hosting with integrated CI/CD pipelines
  • 2Organizations with compliance requirements needing air-gapped CI/CD infrastructure
  • 3Microservices architectures requiring automated Docker image builds and registry management
  • 4Open source projects needing cost-effective CI/CD without GitHub Actions minute limitations
  • 5Development teams practicing GitOps with automated testing and deployment to Kubernetes clusters
  • 6Startups and small companies wanting GitHub Enterprise features at zero recurring cost
  • 7Educational institutions teaching DevOps practices with self-contained development environments

Prerequisites

  • Minimum 2GB RAM recommended (512MB for Gitea, 1GB+ for PostgreSQL instances, plus runner overhead)
  • Docker Engine with BuildKit support and docker.sock access for the Drone runner
  • Available ports 3000 (Gitea web), 222 (Gitea SSH), and 8080 (Drone web interface)
  • Domain names or static IPs for DRONE_SERVER_HOST configuration and webhook callbacks
  • Understanding of YAML pipeline syntax and Docker container concepts for build configuration
  • SSL certificates for production deployments to enable secure webhook delivery

For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms

docker-compose.yml

docker-compose.yml
1services:
2 gitea:
3 image: gitea/gitea:latest
4 environment:
5 USER_UID: 1000
6 USER_GID: 1000
7 GITEA__database__DB_TYPE: postgres
8 GITEA__database__HOST: gitea-db:5432
9 GITEA__database__NAME: gitea
10 GITEA__database__USER: ${POSTGRES_USER}
11 GITEA__database__PASSWD: ${POSTGRES_PASSWORD}
12 ports:
13 - "3000:3000"
14 - "222:22"
15 volumes:
16 - gitea_data:/data
17 depends_on:
18 gitea-db:
19 condition: service_healthy
20 networks:
21 - drone-net
22 restart: unless-stopped
23
24 gitea-db:
25 image: postgres:15-alpine
26 environment:
27 POSTGRES_USER: ${POSTGRES_USER}
28 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
29 POSTGRES_DB: gitea
30 volumes:
31 - gitea_postgres:/var/lib/postgresql/data
32 healthcheck:
33 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
34 interval: 10s
35 timeout: 5s
36 retries: 5
37 networks:
38 - drone-net
39 restart: unless-stopped
40
41 drone:
42 image: drone/drone:latest
43 environment:
44 DRONE_GITEA_SERVER: http://gitea:3000
45 DRONE_GITEA_CLIENT_ID: ${DRONE_GITEA_CLIENT_ID}
46 DRONE_GITEA_CLIENT_SECRET: ${DRONE_GITEA_CLIENT_SECRET}
47 DRONE_RPC_SECRET: ${DRONE_RPC_SECRET}
48 DRONE_SERVER_HOST: ${DRONE_SERVER_HOST}
49 DRONE_SERVER_PROTO: http
50 DRONE_DATABASE_DRIVER: postgres
51 DRONE_DATABASE_DATASOURCE: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@drone-db:5432/drone?sslmode=disable
52 ports:
53 - "8080:80"
54 volumes:
55 - drone_data:/data
56 depends_on:
57 drone-db:
58 condition: service_healthy
59 gitea:
60 condition: service_started
61 networks:
62 - drone-net
63 restart: unless-stopped
64
65 drone-db:
66 image: postgres:15-alpine
67 environment:
68 POSTGRES_USER: ${POSTGRES_USER}
69 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
70 POSTGRES_DB: drone
71 volumes:
72 - drone_postgres:/var/lib/postgresql/data
73 healthcheck:
74 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
75 interval: 10s
76 timeout: 5s
77 retries: 5
78 networks:
79 - drone-net
80 restart: unless-stopped
81
82 drone-runner:
83 image: drone/drone-runner-docker:latest
84 environment:
85 DRONE_RPC_PROTO: http
86 DRONE_RPC_HOST: drone
87 DRONE_RPC_SECRET: ${DRONE_RPC_SECRET}
88 DRONE_RUNNER_CAPACITY: 2
89 DRONE_RUNNER_NAME: docker-runner
90 volumes:
91 - /var/run/docker.sock:/var/run/docker.sock
92 depends_on:
93 - drone
94 networks:
95 - drone-net
96 restart: unless-stopped
97
98volumes:
99 gitea_data:
100 gitea_postgres:
101 drone_data:
102 drone_postgres:
103
104networks:
105 drone-net:
106 driver: bridge

.env Template

.env
1# PostgreSQL (shared)
2POSTGRES_USER=drone
3POSTGRES_PASSWORD=secure_postgres_password
4
5# Drone Server
6DRONE_SERVER_HOST=localhost:8080
7DRONE_RPC_SECRET=your_rpc_secret_here
8
9# Gitea OAuth2 (create in Gitea Settings > Applications)
10DRONE_GITEA_CLIENT_ID=your_client_id
11DRONE_GITEA_CLIENT_SECRET=your_client_secret

Usage Notes

  1. 1Gitea at http://localhost:3000
  2. 2Drone at http://localhost:8080
  3. 3Create OAuth2 app in Gitea first
  4. 4Add .drone.yml to repos for pipelines

Individual Services(5 services)

Copy individual services to mix and match with your existing compose files.

gitea
gitea:
  image: gitea/gitea:latest
  environment:
    USER_UID: 1000
    USER_GID: 1000
    GITEA__database__DB_TYPE: postgres
    GITEA__database__HOST: gitea-db:5432
    GITEA__database__NAME: gitea
    GITEA__database__USER: ${POSTGRES_USER}
    GITEA__database__PASSWD: ${POSTGRES_PASSWORD}
  ports:
    - "3000:3000"
    - "222:22"
  volumes:
    - gitea_data:/data
  depends_on:
    gitea-db:
      condition: service_healthy
  networks:
    - drone-net
  restart: unless-stopped
gitea-db
gitea-db:
  image: postgres:15-alpine
  environment:
    POSTGRES_USER: ${POSTGRES_USER}
    POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    POSTGRES_DB: gitea
  volumes:
    - gitea_postgres:/var/lib/postgresql/data
  healthcheck:
    test:
      - CMD-SHELL
      - pg_isready -U ${POSTGRES_USER}
    interval: 10s
    timeout: 5s
    retries: 5
  networks:
    - drone-net
  restart: unless-stopped
drone
drone:
  image: drone/drone:latest
  environment:
    DRONE_GITEA_SERVER: http://gitea:3000
    DRONE_GITEA_CLIENT_ID: ${DRONE_GITEA_CLIENT_ID}
    DRONE_GITEA_CLIENT_SECRET: ${DRONE_GITEA_CLIENT_SECRET}
    DRONE_RPC_SECRET: ${DRONE_RPC_SECRET}
    DRONE_SERVER_HOST: ${DRONE_SERVER_HOST}
    DRONE_SERVER_PROTO: http
    DRONE_DATABASE_DRIVER: postgres
    DRONE_DATABASE_DATASOURCE: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@drone-db:5432/drone?sslmode=disable
  ports:
    - "8080:80"
  volumes:
    - drone_data:/data
  depends_on:
    drone-db:
      condition: service_healthy
    gitea:
      condition: service_started
  networks:
    - drone-net
  restart: unless-stopped
drone-db
drone-db:
  image: postgres:15-alpine
  environment:
    POSTGRES_USER: ${POSTGRES_USER}
    POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    POSTGRES_DB: drone
  volumes:
    - drone_postgres:/var/lib/postgresql/data
  healthcheck:
    test:
      - CMD-SHELL
      - pg_isready -U ${POSTGRES_USER}
    interval: 10s
    timeout: 5s
    retries: 5
  networks:
    - drone-net
  restart: unless-stopped
drone-runner
drone-runner:
  image: drone/drone-runner-docker:latest
  environment:
    DRONE_RPC_PROTO: http
    DRONE_RPC_HOST: drone
    DRONE_RPC_SECRET: ${DRONE_RPC_SECRET}
    DRONE_RUNNER_CAPACITY: 2
    DRONE_RUNNER_NAME: docker-runner
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock
  depends_on:
    - drone
  networks:
    - drone-net
  restart: unless-stopped

Quick Start

terminal
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 gitea:
5 image: gitea/gitea:latest
6 environment:
7 USER_UID: 1000
8 USER_GID: 1000
9 GITEA__database__DB_TYPE: postgres
10 GITEA__database__HOST: gitea-db:5432
11 GITEA__database__NAME: gitea
12 GITEA__database__USER: ${POSTGRES_USER}
13 GITEA__database__PASSWD: ${POSTGRES_PASSWORD}
14 ports:
15 - "3000:3000"
16 - "222:22"
17 volumes:
18 - gitea_data:/data
19 depends_on:
20 gitea-db:
21 condition: service_healthy
22 networks:
23 - drone-net
24 restart: unless-stopped
25
26 gitea-db:
27 image: postgres:15-alpine
28 environment:
29 POSTGRES_USER: ${POSTGRES_USER}
30 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
31 POSTGRES_DB: gitea
32 volumes:
33 - gitea_postgres:/var/lib/postgresql/data
34 healthcheck:
35 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
36 interval: 10s
37 timeout: 5s
38 retries: 5
39 networks:
40 - drone-net
41 restart: unless-stopped
42
43 drone:
44 image: drone/drone:latest
45 environment:
46 DRONE_GITEA_SERVER: http://gitea:3000
47 DRONE_GITEA_CLIENT_ID: ${DRONE_GITEA_CLIENT_ID}
48 DRONE_GITEA_CLIENT_SECRET: ${DRONE_GITEA_CLIENT_SECRET}
49 DRONE_RPC_SECRET: ${DRONE_RPC_SECRET}
50 DRONE_SERVER_HOST: ${DRONE_SERVER_HOST}
51 DRONE_SERVER_PROTO: http
52 DRONE_DATABASE_DRIVER: postgres
53 DRONE_DATABASE_DATASOURCE: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@drone-db:5432/drone?sslmode=disable
54 ports:
55 - "8080:80"
56 volumes:
57 - drone_data:/data
58 depends_on:
59 drone-db:
60 condition: service_healthy
61 gitea:
62 condition: service_started
63 networks:
64 - drone-net
65 restart: unless-stopped
66
67 drone-db:
68 image: postgres:15-alpine
69 environment:
70 POSTGRES_USER: ${POSTGRES_USER}
71 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
72 POSTGRES_DB: drone
73 volumes:
74 - drone_postgres:/var/lib/postgresql/data
75 healthcheck:
76 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
77 interval: 10s
78 timeout: 5s
79 retries: 5
80 networks:
81 - drone-net
82 restart: unless-stopped
83
84 drone-runner:
85 image: drone/drone-runner-docker:latest
86 environment:
87 DRONE_RPC_PROTO: http
88 DRONE_RPC_HOST: drone
89 DRONE_RPC_SECRET: ${DRONE_RPC_SECRET}
90 DRONE_RUNNER_CAPACITY: 2
91 DRONE_RUNNER_NAME: docker-runner
92 volumes:
93 - /var/run/docker.sock:/var/run/docker.sock
94 depends_on:
95 - drone
96 networks:
97 - drone-net
98 restart: unless-stopped
99
100volumes:
101 gitea_data:
102 gitea_postgres:
103 drone_data:
104 drone_postgres:
105
106networks:
107 drone-net:
108 driver: bridge
109EOF
110
111# 2. Create the .env file
112cat > .env << 'EOF'
113# PostgreSQL (shared)
114POSTGRES_USER=drone
115POSTGRES_PASSWORD=secure_postgres_password
116
117# Drone Server
118DRONE_SERVER_HOST=localhost:8080
119DRONE_RPC_SECRET=your_rpc_secret_here
120
121# Gitea OAuth2 (create in Gitea Settings > Applications)
122DRONE_GITEA_CLIENT_ID=your_client_id
123DRONE_GITEA_CLIENT_SECRET=your_client_secret
124EOF
125
126# 3. Start the services
127docker compose up -d
128
129# 4. View logs
130docker compose logs -f

One-Liner

Run this command to download and set up the recipe in one step:

terminal
1curl -fsSL https://docker.recipes/api/recipes/drone-ci-stack/run | bash

Troubleshooting

  • OAuth2 application not found error: Create OAuth2 application in Gitea admin panel with correct redirect URI http://your-drone-host/login
  • Pipeline fails with 'Cannot connect to Docker daemon': Ensure drone-runner container has access to /var/run/docker.sock and Docker socket permissions
  • Gitea webhooks failing with connection refused: Verify DRONE_SERVER_HOST matches accessible hostname from Gitea container perspective
  • Database connection errors on startup: Check PostgreSQL containers are healthy and POSTGRES_USER/POSTGRES_PASSWORD environment variables match across services
  • Build secrets not accessible in pipeline: Verify secrets are added in Drone web UI at repository level and referenced correctly in .drone.yml with from_secret syntax
  • Runner capacity exceeded errors: Increase DRONE_RUNNER_CAPACITY value or deploy additional drone-runner instances with unique DRONE_RUNNER_NAME values

Community Notes

Loading...
Loading notes...

Download Recipe Kit

Get all files in a ready-to-deploy package

Includes docker-compose.yml, .env template, README, and license

Ad Space