Drone CI with Gitea
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:latest4 environment: 5 USER_UID: 10006 USER_GID: 10007 GITEA__database__DB_TYPE: postgres8 GITEA__database__HOST: gitea-db:54329 GITEA__database__NAME: gitea10 GITEA__database__USER: ${POSTGRES_USER}11 GITEA__database__PASSWD: ${POSTGRES_PASSWORD}12 ports: 13 - "3000:3000"14 - "222:22"15 volumes: 16 - gitea_data:/data17 depends_on: 18 gitea-db: 19 condition: service_healthy20 networks: 21 - drone-net22 restart: unless-stopped2324 gitea-db: 25 image: postgres:15-alpine26 environment: 27 POSTGRES_USER: ${POSTGRES_USER}28 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}29 POSTGRES_DB: gitea30 volumes: 31 - gitea_postgres:/var/lib/postgresql/data32 healthcheck: 33 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]34 interval: 10s35 timeout: 5s36 retries: 537 networks: 38 - drone-net39 restart: unless-stopped4041 drone: 42 image: drone/drone:latest43 environment: 44 DRONE_GITEA_SERVER: http://gitea:300045 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: http50 DRONE_DATABASE_DRIVER: postgres51 DRONE_DATABASE_DATASOURCE: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@drone-db:5432/drone?sslmode=disable52 ports: 53 - "8080:80"54 volumes: 55 - drone_data:/data56 depends_on: 57 drone-db: 58 condition: service_healthy59 gitea: 60 condition: service_started61 networks: 62 - drone-net63 restart: unless-stopped6465 drone-db: 66 image: postgres:15-alpine67 environment: 68 POSTGRES_USER: ${POSTGRES_USER}69 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}70 POSTGRES_DB: drone71 volumes: 72 - drone_postgres:/var/lib/postgresql/data73 healthcheck: 74 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]75 interval: 10s76 timeout: 5s77 retries: 578 networks: 79 - drone-net80 restart: unless-stopped8182 drone-runner: 83 image: drone/drone-runner-docker:latest84 environment: 85 DRONE_RPC_PROTO: http86 DRONE_RPC_HOST: drone87 DRONE_RPC_SECRET: ${DRONE_RPC_SECRET}88 DRONE_RUNNER_CAPACITY: 289 DRONE_RUNNER_NAME: docker-runner90 volumes: 91 - /var/run/docker.sock:/var/run/docker.sock92 depends_on: 93 - drone94 networks: 95 - drone-net96 restart: unless-stopped9798volumes: 99 gitea_data: 100 gitea_postgres: 101 drone_data: 102 drone_postgres: 103104networks: 105 drone-net: 106 driver: bridge.env Template
.env
1# PostgreSQL (shared)2POSTGRES_USER=drone3POSTGRES_PASSWORD=secure_postgres_password45# Drone Server6DRONE_SERVER_HOST=localhost:80807DRONE_RPC_SECRET=your_rpc_secret_here89# Gitea OAuth2 (create in Gitea Settings > Applications)10DRONE_GITEA_CLIENT_ID=your_client_id11DRONE_GITEA_CLIENT_SECRET=your_client_secretUsage Notes
- 1Gitea at http://localhost:3000
- 2Drone at http://localhost:8080
- 3Create OAuth2 app in Gitea first
- 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 file2cat > docker-compose.yml << 'EOF'3services:4 gitea:5 image: gitea/gitea:latest6 environment:7 USER_UID: 10008 USER_GID: 10009 GITEA__database__DB_TYPE: postgres10 GITEA__database__HOST: gitea-db:543211 GITEA__database__NAME: gitea12 GITEA__database__USER: ${POSTGRES_USER}13 GITEA__database__PASSWD: ${POSTGRES_PASSWORD}14 ports:15 - "3000:3000"16 - "222:22"17 volumes:18 - gitea_data:/data19 depends_on:20 gitea-db:21 condition: service_healthy22 networks:23 - drone-net24 restart: unless-stopped2526 gitea-db:27 image: postgres:15-alpine28 environment:29 POSTGRES_USER: ${POSTGRES_USER}30 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}31 POSTGRES_DB: gitea32 volumes:33 - gitea_postgres:/var/lib/postgresql/data34 healthcheck:35 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]36 interval: 10s37 timeout: 5s38 retries: 539 networks:40 - drone-net41 restart: unless-stopped4243 drone:44 image: drone/drone:latest45 environment:46 DRONE_GITEA_SERVER: http://gitea:300047 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: http52 DRONE_DATABASE_DRIVER: postgres53 DRONE_DATABASE_DATASOURCE: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@drone-db:5432/drone?sslmode=disable54 ports:55 - "8080:80"56 volumes:57 - drone_data:/data58 depends_on:59 drone-db:60 condition: service_healthy61 gitea:62 condition: service_started63 networks:64 - drone-net65 restart: unless-stopped6667 drone-db:68 image: postgres:15-alpine69 environment:70 POSTGRES_USER: ${POSTGRES_USER}71 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}72 POSTGRES_DB: drone73 volumes:74 - drone_postgres:/var/lib/postgresql/data75 healthcheck:76 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]77 interval: 10s78 timeout: 5s79 retries: 580 networks:81 - drone-net82 restart: unless-stopped8384 drone-runner:85 image: drone/drone-runner-docker:latest86 environment:87 DRONE_RPC_PROTO: http88 DRONE_RPC_HOST: drone89 DRONE_RPC_SECRET: ${DRONE_RPC_SECRET}90 DRONE_RUNNER_CAPACITY: 291 DRONE_RUNNER_NAME: docker-runner92 volumes:93 - /var/run/docker.sock:/var/run/docker.sock94 depends_on:95 - drone96 networks:97 - drone-net98 restart: unless-stopped99100volumes:101 gitea_data:102 gitea_postgres:103 drone_data:104 drone_postgres:105106networks:107 drone-net:108 driver: bridge109EOF110111# 2. Create the .env file112cat > .env << 'EOF'113# PostgreSQL (shared)114POSTGRES_USER=drone115POSTGRES_PASSWORD=secure_postgres_password116117# Drone Server118DRONE_SERVER_HOST=localhost:8080119DRONE_RPC_SECRET=your_rpc_secret_here120121# Gitea OAuth2 (create in Gitea Settings > Applications)122DRONE_GITEA_CLIENT_ID=your_client_id123DRONE_GITEA_CLIENT_SECRET=your_client_secret124EOF125126# 3. Start the services127docker compose up -d128129# 4. View logs130docker compose logs -fOne-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 | bashTroubleshooting
- 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
Components
drone-serverdrone-runnergiteapostgresql
Tags
#drone#ci-cd#gitea#docker#pipelines
Category
DevOps & CI/CDAd Space
Shortcuts: C CopyF FavoriteD Download