- Prerequisites
- Step 1: Prepare Environment
- Step 2: Configure Docker Desktop
- Step 3: Generate Secrets
- Step 4: Start Core Services
- Step 5: Configure Gitea
- Step 6: Create OAuth Application
- Step 7: Complete Drone Setup
- Step 8: Verify Installation
- Troubleshooting
| Software | Minimum Version | Download Link |
|---|---|---|
| Docker Desktop | 4.0+ | https://www.docker.com/products/docker-desktop |
| Git | 2.30+ | https://git-scm.com/downloads |
| PowerShell | 5.1+ | Built into Windows |
- OS: Windows 10/11 Pro or Enterprise (with WSL2)
- RAM: 8 GB minimum (16 GB recommended)
- Disk: 20 GB free space
- CPU: 4 cores minimum
Open PowerShell and run:
# Check Docker version
docker --version
# Check Docker Compose version
docker compose version
# Verify Docker is running
docker psExpected output:
Docker version 24.x.x, build xxxxxxx
Docker Compose version v2.x.x
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# Navigate to your desired location
cd "C:\Users\keeh\OneDrive - Mansoft\Skrivebord\Mock Servers\ci-co"
# Verify files exist
Get-ChildItem# Copy the example environment file
Copy-Item .env.example .env
# Open in notepad to edit
notepad .env- Open Docker Desktop
- Go to Settings (gear icon)
- Navigate to Resources > Advanced
- Configure resources:
- CPUs: 4 (or more)
- Memory: 4 GB (or more)
- Swap: 1 GB
- Disk image size: 60 GB+
- Go to Settings > General
- Ensure Use the WSL 2 based engine is checked
- Click Apply & Restart
For the local registry to work without HTTPS:
- Go to Settings > Docker Engine
- Add the following to the JSON configuration:
{
"insecure-registries": ["localhost:5000", "127.0.0.1:5000"]
}- Click Apply & Restart
Open PowerShell and run:
# Generate PostgreSQL password
$POSTGRES_PASSWORD = -join ((48..57) + (65..90) + (97..122) | Get-Random -Count 32 | ForEach-Object {[char]$_})
Write-Host "POSTGRES_PASSWORD=$POSTGRES_PASSWORD"
# Generate Gitea secret key
$GITEA_SECRET = -join ((48..57) + (65..90) + (97..122) | Get-Random -Count 64 | ForEach-Object {[char]$_})
Write-Host "GITEA_SECRET_KEY=$GITEA_SECRET"
# Generate Drone RPC secret
$DRONE_RPC = -join ((48..57) + (65..90) + (97..122) | Get-Random -Count 32 | ForEach-Object {[char]$_})
Write-Host "DRONE_RPC_SECRET=$DRONE_RPC"Edit the .env file with the generated values:
notepad .envUpdate these values (leave DRONE_GITEA_CLIENT_ID and DRONE_GITEA_CLIENT_SECRET empty for now):
POSTGRES_PASSWORD=<your_generated_postgres_password>
GITEA_SECRET_KEY=<your_generated_gitea_secret>
DRONE_RPC_SECRET=<your_generated_drone_rpc_secret>
DRONE_GITEA_CLIENT_ID=
DRONE_GITEA_CLIENT_SECRET=
DRONE_ADMIN_USER=admin# Navigate to the project directory
cd "C:\Users\keeh\OneDrive - Mansoft\Skrivebord\Mock Servers\ci-co"
# Start only Gitea and PostgreSQL first
docker compose up -d postgres gitea registry registry-ui
# Wait for services to be healthy
docker compose ps
# View logs if needed
docker compose logs -f giteaOpen your browser and navigate to: http://localhost:3000
You should see the Gitea initial configuration page.
When you first access http://localhost:3000, you'll see the installation page.
Database Settings (should be pre-filled):
- Database Type:
PostgreSQL - Host:
postgres:5432 - Username:
gitea - Password: (your POSTGRES_PASSWORD)
- Database Name:
gitea
General Settings:
- Site Title:
Local CI/CD - Repository Root Path:
/data/git/repositories - Git LFS Root Path:
/data/git/lfs - Run As Username:
git - SSH Server Domain:
localhost - SSH Server Port:
2222 - Gitea HTTP Listen Port:
3000 - Gitea Base URL:
http://localhost:3000/
Optional Settings:
- Enable Local Mode
- Disable Gravatar
- Enable Open ID Sign-In (optional)
On the same page, scroll down to Administrator Account Settings:
- Administrator Username:
admin(or your preferred username) - Password:
<strong_password> - Confirm Password:
<strong_password> - Email Address:
admin@localhost.local
Click Install Gitea
- Click Sign In
- Enter your admin credentials
- Verify you can access the dashboard
- Log into Gitea as admin
- Click your profile icon (top right) → Settings
- Go to Applications tab
- Under Manage OAuth2 Applications, click Create a new OAuth2 Application
Fill in:
- Application Name:
Drone CI - Redirect URIs:
http://localhost:8080/login
Click Create Application
IMPORTANT: Copy the Client ID and Client Secret immediately. The secret is only shown once!
Client ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Client Secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
notepad .envAdd the OAuth credentials:
DRONE_GITEA_CLIENT_ID=<your_client_id>
DRONE_GITEA_CLIENT_SECRET=<your_client_secret>
DRONE_ADMIN_USER=admin# Stop current services
docker compose down
# Start all services
docker compose up -d
# Verify all services are running
docker compose psExpected output:
NAME IMAGE STATUS PORTS
ci-drone-runner drone/drone-runner-docker:1 Up
ci-drone-server drone/drone:2 Up 0.0.0.0:8080->80/tcp
ci-gitea gitea/gitea:latest Up (healthy) 0.0.0.0:2222->22/tcp, 0.0.0.0:3000->3000/tcp
ci-postgres postgres:15-alpine Up (healthy) 5432/tcp
ci-registry registry:2 Up 0.0.0.0:5000->5000/tcp
ci-registry-ui joxit/docker-registry-ui:latest Up 0.0.0.0:5001->80/tcp
- Open http://localhost:8080 in your browser
- Click Continue to authenticate with Gitea
- You'll be redirected to Gitea's authorization page
- Click Authorize Application
- You'll be redirected back to Drone CI dashboard
- In Drone, click Sync to fetch repositories from Gitea
- Find a repository and click Activate
- Configure repository settings as needed
Run the verification script:
# Check service status
docker compose ps
# Check service health
docker compose exec gitea curl -s http://localhost:3000/api/healthz
docker compose exec drone-server wget -qO- http://localhost/healthz
# Check registry
curl http://localhost:5000/v2/_catalog| Service | URL | Purpose |
|---|---|---|
| Gitea | http://localhost:3000 | Git repository management |
| Drone CI | http://localhost:8080 | CI/CD pipelines |
| Registry | http://localhost:5000 | Docker image storage |
| Registry UI | http://localhost:5001 | Browse stored images |
# Pull a test image
docker pull hello-world
# Tag it for local registry
docker tag hello-world localhost:5000/hello-world:latest
# Push to local registry
docker push localhost:5000/hello-world:latest
# Verify it's in the registry
curl http://localhost:5000/v2/_catalogExpected output:
{"repositories":["hello-world"]}# Check PostgreSQL logs
docker compose logs postgres
# Verify PostgreSQL is healthy
docker compose exec postgres pg_isready -U gitea# Check Drone server logs
docker compose logs drone-server
# Verify Gitea is accessible from Drone container
docker compose exec drone-server wget -qO- http://gitea:3000/api/healthz# Check runner logs
docker compose logs drone-runner
# Verify RPC secret matches
docker compose exec drone-runner env | grep DRONE_RPC
docker compose exec drone-server env | grep DRONE_RPC- Ensure Docker Desktop has
localhost:5000in insecure registries - Restart Docker Desktop after adding the insecure registry
# Find what's using the port (e.g., 3000)
netstat -ano | findstr :3000
# Kill the process if needed
taskkill /PID <process_id> /FTo completely reset and start fresh:
# Stop all services
docker compose down
# Remove all volumes (WARNING: This deletes all data!)
docker compose down -v
# Remove all images
docker compose down --rmi all
# Start fresh
docker compose up -d# All services
docker compose logs -f
# Specific service
docker compose logs -f gitea
docker compose logs -f drone-server
docker compose logs -f drone-runnerAfter successful installation:
- Read the CONFIGURATION.md for advanced setup
- Read the USAGE.md for workflows and examples
- Create your first repository in Gitea
- Add a
.drone.ymlto enable CI/CD