docker.recipes

Gitea + Drone CI + Registry

advanced

Complete self-hosted Git platform with CI/CD pipeline and container registry.

Overview

Gitea is a lightweight, self-hosted Git service written in Go that provides GitHub-like functionality with minimal resource requirements. Originally forked from Gogs in 2016, Gitea has evolved into a full-featured Git platform offering repository hosting, issue tracking, pull requests, and built-in package registry support. Its philosophy centers on simplicity and performance, making it ideal for teams who want GitHub's features without the complexity of GitLab or dependency on external services. This stack combines Gitea with Drone CI and a Docker Registry to create a complete DevOps pipeline. Drone CI acts as the continuous integration engine, automatically building and testing code when developers push to Gitea repositories. The Docker Registry stores the resulting container images, while Drone runners execute builds in isolated Docker containers. This integration creates an automated workflow where code commits trigger builds, tests run in consistent environments, and successful builds produce deployable artifacts stored in your private registry. Development teams, DevOps engineers, and organizations prioritizing data sovereignty will find this stack particularly valuable. Unlike cloud-based solutions, this combination runs entirely on your infrastructure, ensuring code privacy and eliminating vendor lock-in. Small to medium teams benefit from Gitea's lightweight nature (requiring only 512MB RAM), while the Drone CI integration provides enterprise-grade automation without the licensing costs of Jenkins or GitLab CI. The included Docker Registry completes the picture by providing secure, private container image storage with configurable retention policies.

Key Features

  • GitHub-like web interface with pull requests, code review, and issue tracking
  • PostgreSQL backend for both Gitea and Drone ensuring data consistency and reliability
  • Drone CI pipeline automation with YAML-based configuration and Docker container execution
  • Docker Registry v2 API compliance with image deletion and garbage collection support
  • OAuth2 integration between Gitea and Drone for single sign-on authentication
  • Multi-architecture container support through Drone's Docker runner capabilities
  • Webhook-triggered builds automatically starting CI pipelines on Git push events
  • Built-in package registry support in Gitea for npm, Docker, and Maven artifacts

Common Use Cases

  • 1Private software development teams needing GitHub-like collaboration without SaaS dependencies
  • 2Organizations with compliance requirements mandating on-premises code hosting and CI/CD
  • 3Startups building containerized applications requiring integrated Git hosting and image registry
  • 4Development teams migrating from GitHub/GitLab seeking cost-effective self-hosted alternatives
  • 5Educational institutions teaching DevOps practices with complete pipeline visibility
  • 6Open source projects requiring private development repositories with public release automation
  • 7Consultancies managing multiple client codebases with isolated, branded Git platforms

Prerequisites

  • Docker host with minimum 2GB RAM (1GB for services, 1GB for build processes)
  • Available ports 3000 (Gitea web), 222 (SSH), 8080 (Drone), and 5000 (Registry)
  • Domain names or hostnames configured for Gitea and Drone with SSL certificates recommended
  • Basic understanding of OAuth2 application setup for Gitea-Drone integration
  • Familiarity with YAML syntax for creating Drone CI pipeline configurations
  • PostgreSQL administration knowledge for database maintenance and backups

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 container_name: gitea
5 environment:
6 - USER_UID=1000
7 - USER_GID=1000
8 - GITEA__database__DB_TYPE=postgres
9 - GITEA__database__HOST=gitea-db:5432
10 - GITEA__database__NAME=gitea
11 - GITEA__database__USER=gitea
12 - GITEA__database__PASSWD=${DB_PASSWORD}
13 - GITEA__server__ROOT_URL=${GITEA_URL}
14 - GITEA__server__SSH_DOMAIN=${GITEA_DOMAIN}
15 - GITEA__server__SSH_PORT=222
16 volumes:
17 - gitea-data:/data
18 - /etc/timezone:/etc/timezone:ro
19 - /etc/localtime:/etc/localtime:ro
20 ports:
21 - "3000:3000"
22 - "222:22"
23 depends_on:
24 - gitea-db
25 networks:
26 - gitea-network
27 restart: unless-stopped
28
29 gitea-db:
30 image: postgres:15-alpine
31 container_name: gitea-db
32 environment:
33 - POSTGRES_USER=gitea
34 - POSTGRES_PASSWORD=${DB_PASSWORD}
35 - POSTGRES_DB=gitea
36 volumes:
37 - gitea-postgres:/var/lib/postgresql/data
38 networks:
39 - gitea-network
40 restart: unless-stopped
41
42 drone:
43 image: drone/drone:2
44 container_name: drone
45 environment:
46 - DRONE_GITEA_SERVER=${GITEA_URL}
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_HOST}
51 - DRONE_SERVER_PROTO=https
52 - DRONE_DATABASE_DRIVER=postgres
53 - DRONE_DATABASE_DATASOURCE=postgres://drone:${DRONE_DB_PASSWORD}@drone-db:5432/drone?sslmode=disable
54 volumes:
55 - drone-data:/data
56 ports:
57 - "8080:80"
58 depends_on:
59 - drone-db
60 networks:
61 - gitea-network
62 restart: unless-stopped
63
64 drone-db:
65 image: postgres:15-alpine
66 container_name: drone-db
67 environment:
68 - POSTGRES_USER=drone
69 - POSTGRES_PASSWORD=${DRONE_DB_PASSWORD}
70 - POSTGRES_DB=drone
71 volumes:
72 - drone-postgres:/var/lib/postgresql/data
73 networks:
74 - gitea-network
75 restart: unless-stopped
76
77 drone-runner:
78 image: drone/drone-runner-docker:1
79 container_name: drone-runner
80 environment:
81 - DRONE_RPC_PROTO=http
82 - DRONE_RPC_HOST=drone
83 - DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
84 - DRONE_RUNNER_CAPACITY=2
85 - DRONE_RUNNER_NAME=docker-runner
86 volumes:
87 - /var/run/docker.sock:/var/run/docker.sock
88 depends_on:
89 - drone
90 networks:
91 - gitea-network
92 restart: unless-stopped
93
94 registry:
95 image: registry:2
96 container_name: registry
97 environment:
98 - REGISTRY_HTTP_SECRET=${REGISTRY_SECRET}
99 - REGISTRY_STORAGE_DELETE_ENABLED=true
100 volumes:
101 - registry-data:/var/lib/registry
102 ports:
103 - "5000:5000"
104 networks:
105 - gitea-network
106 restart: unless-stopped
107
108volumes:
109 gitea-data:
110 gitea-postgres:
111 drone-data:
112 drone-postgres:
113 registry-data:
114
115networks:
116 gitea-network:
117 driver: bridge

.env Template

.env
1# Gitea + Drone CI + Registry
2DB_PASSWORD=secure_gitea_password
3GITEA_URL=http://localhost:3000
4GITEA_DOMAIN=localhost
5
6# Drone CI
7DRONE_GITEA_CLIENT_ID=your_oauth_client_id
8DRONE_GITEA_CLIENT_SECRET=your_oauth_client_secret
9DRONE_RPC_SECRET=super_secret_rpc_key
10DRONE_HOST=localhost:8080
11DRONE_DB_PASSWORD=secure_drone_password
12
13# Registry
14REGISTRY_SECRET=registry_http_secret

Usage Notes

  1. 1Gitea at http://localhost:3000
  2. 2Drone CI at http://localhost:8080
  3. 3Registry at http://localhost:5000
  4. 4Create OAuth app in Gitea for Drone
  5. 5Configure webhooks for CI triggers

Individual Services(6 services)

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

gitea
gitea:
  image: gitea/gitea:latest
  container_name: gitea
  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=gitea
    - GITEA__database__PASSWD=${DB_PASSWORD}
    - GITEA__server__ROOT_URL=${GITEA_URL}
    - GITEA__server__SSH_DOMAIN=${GITEA_DOMAIN}
    - GITEA__server__SSH_PORT=222
  volumes:
    - gitea-data:/data
    - /etc/timezone:/etc/timezone:ro
    - /etc/localtime:/etc/localtime:ro
  ports:
    - "3000:3000"
    - "222:22"
  depends_on:
    - gitea-db
  networks:
    - gitea-network
  restart: unless-stopped
gitea-db
gitea-db:
  image: postgres:15-alpine
  container_name: gitea-db
  environment:
    - POSTGRES_USER=gitea
    - POSTGRES_PASSWORD=${DB_PASSWORD}
    - POSTGRES_DB=gitea
  volumes:
    - gitea-postgres:/var/lib/postgresql/data
  networks:
    - gitea-network
  restart: unless-stopped
drone
drone:
  image: drone/drone:2
  container_name: drone
  environment:
    - DRONE_GITEA_SERVER=${GITEA_URL}
    - 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_HOST}
    - DRONE_SERVER_PROTO=https
    - DRONE_DATABASE_DRIVER=postgres
    - DRONE_DATABASE_DATASOURCE=postgres://drone:${DRONE_DB_PASSWORD}@drone-db:5432/drone?sslmode=disable
  volumes:
    - drone-data:/data
  ports:
    - "8080:80"
  depends_on:
    - drone-db
  networks:
    - gitea-network
  restart: unless-stopped
drone-db
drone-db:
  image: postgres:15-alpine
  container_name: drone-db
  environment:
    - POSTGRES_USER=drone
    - POSTGRES_PASSWORD=${DRONE_DB_PASSWORD}
    - POSTGRES_DB=drone
  volumes:
    - drone-postgres:/var/lib/postgresql/data
  networks:
    - gitea-network
  restart: unless-stopped
drone-runner
drone-runner:
  image: drone/drone-runner-docker:1
  container_name: drone-runner
  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:
    - gitea-network
  restart: unless-stopped
registry
registry:
  image: registry:2
  container_name: registry
  environment:
    - REGISTRY_HTTP_SECRET=${REGISTRY_SECRET}
    - REGISTRY_STORAGE_DELETE_ENABLED=true
  volumes:
    - registry-data:/var/lib/registry
  ports:
    - "5000:5000"
  networks:
    - gitea-network
  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 container_name: gitea
7 environment:
8 - USER_UID=1000
9 - USER_GID=1000
10 - GITEA__database__DB_TYPE=postgres
11 - GITEA__database__HOST=gitea-db:5432
12 - GITEA__database__NAME=gitea
13 - GITEA__database__USER=gitea
14 - GITEA__database__PASSWD=${DB_PASSWORD}
15 - GITEA__server__ROOT_URL=${GITEA_URL}
16 - GITEA__server__SSH_DOMAIN=${GITEA_DOMAIN}
17 - GITEA__server__SSH_PORT=222
18 volumes:
19 - gitea-data:/data
20 - /etc/timezone:/etc/timezone:ro
21 - /etc/localtime:/etc/localtime:ro
22 ports:
23 - "3000:3000"
24 - "222:22"
25 depends_on:
26 - gitea-db
27 networks:
28 - gitea-network
29 restart: unless-stopped
30
31 gitea-db:
32 image: postgres:15-alpine
33 container_name: gitea-db
34 environment:
35 - POSTGRES_USER=gitea
36 - POSTGRES_PASSWORD=${DB_PASSWORD}
37 - POSTGRES_DB=gitea
38 volumes:
39 - gitea-postgres:/var/lib/postgresql/data
40 networks:
41 - gitea-network
42 restart: unless-stopped
43
44 drone:
45 image: drone/drone:2
46 container_name: drone
47 environment:
48 - DRONE_GITEA_SERVER=${GITEA_URL}
49 - DRONE_GITEA_CLIENT_ID=${DRONE_GITEA_CLIENT_ID}
50 - DRONE_GITEA_CLIENT_SECRET=${DRONE_GITEA_CLIENT_SECRET}
51 - DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
52 - DRONE_SERVER_HOST=${DRONE_HOST}
53 - DRONE_SERVER_PROTO=https
54 - DRONE_DATABASE_DRIVER=postgres
55 - DRONE_DATABASE_DATASOURCE=postgres://drone:${DRONE_DB_PASSWORD}@drone-db:5432/drone?sslmode=disable
56 volumes:
57 - drone-data:/data
58 ports:
59 - "8080:80"
60 depends_on:
61 - drone-db
62 networks:
63 - gitea-network
64 restart: unless-stopped
65
66 drone-db:
67 image: postgres:15-alpine
68 container_name: drone-db
69 environment:
70 - POSTGRES_USER=drone
71 - POSTGRES_PASSWORD=${DRONE_DB_PASSWORD}
72 - POSTGRES_DB=drone
73 volumes:
74 - drone-postgres:/var/lib/postgresql/data
75 networks:
76 - gitea-network
77 restart: unless-stopped
78
79 drone-runner:
80 image: drone/drone-runner-docker:1
81 container_name: drone-runner
82 environment:
83 - DRONE_RPC_PROTO=http
84 - DRONE_RPC_HOST=drone
85 - DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
86 - DRONE_RUNNER_CAPACITY=2
87 - DRONE_RUNNER_NAME=docker-runner
88 volumes:
89 - /var/run/docker.sock:/var/run/docker.sock
90 depends_on:
91 - drone
92 networks:
93 - gitea-network
94 restart: unless-stopped
95
96 registry:
97 image: registry:2
98 container_name: registry
99 environment:
100 - REGISTRY_HTTP_SECRET=${REGISTRY_SECRET}
101 - REGISTRY_STORAGE_DELETE_ENABLED=true
102 volumes:
103 - registry-data:/var/lib/registry
104 ports:
105 - "5000:5000"
106 networks:
107 - gitea-network
108 restart: unless-stopped
109
110volumes:
111 gitea-data:
112 gitea-postgres:
113 drone-data:
114 drone-postgres:
115 registry-data:
116
117networks:
118 gitea-network:
119 driver: bridge
120EOF
121
122# 2. Create the .env file
123cat > .env << 'EOF'
124# Gitea + Drone CI + Registry
125DB_PASSWORD=secure_gitea_password
126GITEA_URL=http://localhost:3000
127GITEA_DOMAIN=localhost
128
129# Drone CI
130DRONE_GITEA_CLIENT_ID=your_oauth_client_id
131DRONE_GITEA_CLIENT_SECRET=your_oauth_client_secret
132DRONE_RPC_SECRET=super_secret_rpc_key
133DRONE_HOST=localhost:8080
134DRONE_DB_PASSWORD=secure_drone_password
135
136# Registry
137REGISTRY_SECRET=registry_http_secret
138EOF
139
140# 3. Start the services
141docker compose up -d
142
143# 4. View logs
144docker 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/gitea-drone-registry/run | bash

Troubleshooting

  • Drone shows 'oauth2: cannot fetch token' error: Verify DRONE_GITEA_CLIENT_ID and CLIENT_SECRET match OAuth application created in Gitea settings
  • Gitea SSH clone fails with permission denied: Check USER_UID/USER_GID environment variables match host user permissions on gitea-data volume
  • Drone builds fail with 'Cannot connect to Docker daemon': Ensure /var/run/docker.sock is properly mounted and drone-runner has Docker socket access
  • Registry push fails with 'unauthorized' error: Configure Docker daemon with insecure-registries for localhost:5000 or implement TLS certificates
  • Gitea database connection errors on startup: Verify DB_PASSWORD environment variable matches between gitea and gitea-db services
  • Drone runner shows 'server gave HTTP response to HTTPS client': Check DRONE_RPC_PROTO matches actual protocol used by Drone server

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