$docker.recipes

Gitea with Actions Runner

intermediate

Self-hosted Git with Gitea and integrated CI/CD runners.

[i]Overview

Gitea is a lightweight, self-hosted Git service written in Go that provides GitHub-like functionality without the resource overhead of larger alternatives like GitLab. Originally forked from Gogs in 2016, Gitea has evolved into a mature platform offering repository management, issue tracking, pull requests, and built-in CI/CD through Gitea Actions. This makes it an ideal choice for teams wanting complete control over their source code and development workflow while maintaining familiar GitHub-style interfaces and workflows. This stack combines Gitea with PostgreSQL for robust data persistence, Redis for high-performance caching and session management, and the official Gitea Actions runner for automated CI/CD pipelines. PostgreSQL ensures ACID compliance and handles complex queries efficiently, while Redis dramatically improves Gitea's response times by caching frequently accessed data. The Actions runner provides Docker-in-Docker capabilities, enabling teams to run sophisticated build, test, and deployment pipelines using GitHub Actions-compatible workflow syntax. This configuration targets development teams, DevOps engineers, and organizations requiring private Git hosting with integrated CI/CD capabilities. Small to medium-sized companies benefit from reduced licensing costs compared to hosted solutions, while maintaining enterprise-grade features like OAuth2 authentication, webhook integrations, and scalable runner architecture. The stack is particularly valuable for teams with compliance requirements, air-gapped environments, or those wanting to avoid vendor lock-in while preserving familiar Git workflows.

[*]Key Features

  • [+]GitHub Actions-compatible CI/CD workflows with YAML syntax support
  • [+]PostgreSQL backend providing ACID transactions and advanced querying capabilities
  • [+]Redis caching layer for sub-millisecond session and repository data access
  • [+]Docker-in-Docker runner execution supporting multi-stage builds and custom images
  • [+]Built-in package registry supporting Docker, npm, Maven, and NuGet packages
  • [+]OAuth2 and LDAP authentication with role-based access control
  • [+]Pull request workflows with code review, approvals, and merge protection
  • [+]Issue tracking with milestones, labels, and project boards

[#]Common Use Cases

  • [1]Private source code hosting for proprietary software development
  • [2]Self-hosted CI/CD pipelines replacing Jenkins or GitHub Actions
  • [3]Code review workflows for distributed development teams
  • [4]Package registry for internal libraries and Docker images
  • [5]Migration from GitHub or GitLab to reduce licensing costs
  • [6]Compliance-driven development in regulated industries
  • [7]Air-gapped development environments in secure facilities

[!]Prerequisites

  • [!]Minimum 1GB RAM (2GB+ recommended for active CI/CD workloads)
  • [!]Docker host with /var/run/docker.sock access for runner container
  • [!]Ports 3000 and 2222 available for HTTP and SSH Git access
  • [!]Basic understanding of Git workflows and YAML syntax
  • [!]PostgreSQL administration knowledge for backup and maintenance
  • [!]Environment variables POSTGRES_PASSWORD and RUNNER_TOKEN configured
[!]

WARNING: 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 postgres:
3 image: postgres:15-alpine
4 environment:
5 - POSTGRES_USER=gitea
6 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
7 - POSTGRES_DB=gitea
8 volumes:
9 - postgres_data:/var/lib/postgresql/data
10 networks:
11 - gitea_net
12
13 redis:
14 image: redis:7-alpine
15 volumes:
16 - redis_data:/data
17 networks:
18 - gitea_net
19
20 gitea:
21 image: gitea/gitea:latest
22 ports:
23 - "3000:3000"
24 - "2222:22"
25 environment:
26 - USER_UID=1000
27 - USER_GID=1000
28 - GITEA__database__DB_TYPE=postgres
29 - GITEA__database__HOST=postgres:5432
30 - GITEA__database__NAME=gitea
31 - GITEA__database__USER=gitea
32 - GITEA__database__PASSWD=${POSTGRES_PASSWORD}
33 - GITEA__cache__ADAPTER=redis
34 - GITEA__cache__HOST=redis://redis:6379/0
35 - GITEA__actions__ENABLED=true
36 volumes:
37 - gitea_data:/data
38 - /etc/timezone:/etc/timezone:ro
39 - /etc/localtime:/etc/localtime:ro
40 depends_on:
41 - postgres
42 - redis
43 networks:
44 - gitea_net
45
46 gitea-runner:
47 image: gitea/act_runner:latest
48 environment:
49 - GITEA_INSTANCE_URL=http://gitea:3000
50 - GITEA_RUNNER_REGISTRATION_TOKEN=${RUNNER_TOKEN}
51 - GITEA_RUNNER_NAME=docker-runner
52 volumes:
53 - /var/run/docker.sock:/var/run/docker.sock
54 - runner_data:/data
55 depends_on:
56 - gitea
57 networks:
58 - gitea_net
59
60volumes:
61 postgres_data:
62 redis_data:
63 gitea_data:
64 runner_data:
65
66networks:
67 gitea_net:

[$].env Template

[.env]
1# Gitea with Actions
2POSTGRES_PASSWORD=secure_postgres_password
3RUNNER_TOKEN=your_runner_registration_token
4
5# Gitea at http://localhost:3000
6# SSH at localhost:2222
7# Get runner token from Gitea admin panel

[i]Usage Notes

  1. [1]Gitea at http://localhost:3000
  2. [2]SSH clone via port 2222
  3. [3]Enable Actions in admin settings
  4. [4]Get runner token from Site Admin > Actions > Runners
  5. [5]GitHub Actions compatible workflow files

Individual Services(4 services)

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

postgres
postgres:
  image: postgres:15-alpine
  environment:
    - POSTGRES_USER=gitea
    - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    - POSTGRES_DB=gitea
  volumes:
    - postgres_data:/var/lib/postgresql/data
  networks:
    - gitea_net
redis
redis:
  image: redis:7-alpine
  volumes:
    - redis_data:/data
  networks:
    - gitea_net
gitea
gitea:
  image: gitea/gitea:latest
  ports:
    - "3000:3000"
    - "2222:22"
  environment:
    - USER_UID=1000
    - USER_GID=1000
    - GITEA__database__DB_TYPE=postgres
    - GITEA__database__HOST=postgres:5432
    - GITEA__database__NAME=gitea
    - GITEA__database__USER=gitea
    - GITEA__database__PASSWD=${POSTGRES_PASSWORD}
    - GITEA__cache__ADAPTER=redis
    - GITEA__cache__HOST=redis://redis:6379/0
    - GITEA__actions__ENABLED=true
  volumes:
    - gitea_data:/data
    - /etc/timezone:/etc/timezone:ro
    - /etc/localtime:/etc/localtime:ro
  depends_on:
    - postgres
    - redis
  networks:
    - gitea_net
gitea-runner
gitea-runner:
  image: gitea/act_runner:latest
  environment:
    - GITEA_INSTANCE_URL=http://gitea:3000
    - GITEA_RUNNER_REGISTRATION_TOKEN=${RUNNER_TOKEN}
    - GITEA_RUNNER_NAME=docker-runner
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock
    - runner_data:/data
  depends_on:
    - gitea
  networks:
    - gitea_net

[>]Quick Start

[terminal]
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 postgres:
5 image: postgres:15-alpine
6 environment:
7 - POSTGRES_USER=gitea
8 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
9 - POSTGRES_DB=gitea
10 volumes:
11 - postgres_data:/var/lib/postgresql/data
12 networks:
13 - gitea_net
14
15 redis:
16 image: redis:7-alpine
17 volumes:
18 - redis_data:/data
19 networks:
20 - gitea_net
21
22 gitea:
23 image: gitea/gitea:latest
24 ports:
25 - "3000:3000"
26 - "2222:22"
27 environment:
28 - USER_UID=1000
29 - USER_GID=1000
30 - GITEA__database__DB_TYPE=postgres
31 - GITEA__database__HOST=postgres:5432
32 - GITEA__database__NAME=gitea
33 - GITEA__database__USER=gitea
34 - GITEA__database__PASSWD=${POSTGRES_PASSWORD}
35 - GITEA__cache__ADAPTER=redis
36 - GITEA__cache__HOST=redis://redis:6379/0
37 - GITEA__actions__ENABLED=true
38 volumes:
39 - gitea_data:/data
40 - /etc/timezone:/etc/timezone:ro
41 - /etc/localtime:/etc/localtime:ro
42 depends_on:
43 - postgres
44 - redis
45 networks:
46 - gitea_net
47
48 gitea-runner:
49 image: gitea/act_runner:latest
50 environment:
51 - GITEA_INSTANCE_URL=http://gitea:3000
52 - GITEA_RUNNER_REGISTRATION_TOKEN=${RUNNER_TOKEN}
53 - GITEA_RUNNER_NAME=docker-runner
54 volumes:
55 - /var/run/docker.sock:/var/run/docker.sock
56 - runner_data:/data
57 depends_on:
58 - gitea
59 networks:
60 - gitea_net
61
62volumes:
63 postgres_data:
64 redis_data:
65 gitea_data:
66 runner_data:
67
68networks:
69 gitea_net:
70EOF
71
72# 2. Create the .env file
73cat > .env << 'EOF'
74# Gitea with Actions
75POSTGRES_PASSWORD=secure_postgres_password
76RUNNER_TOKEN=your_runner_registration_token
77
78# Gitea at http://localhost:3000
79# SSH at localhost:2222
80# Get runner token from Gitea admin panel
81EOF
82
83# 3. Start the services
84docker compose up -d
85
86# 4. View logs
87docker 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-actions/run | bash

[?]Troubleshooting

  • [!]Runner registration fails with 'token invalid': Generate new runner token from Site Admin > Actions > Runners and update RUNNER_TOKEN environment variable
  • [!]Actions workflows stuck in 'waiting' status: Verify runner container has access to Docker socket and check runner logs for connection issues
  • [!]Gitea shows 'database connection failed': Ensure PostgreSQL container is healthy and POSTGRES_PASSWORD matches between services
  • [!]SSH clone fails on port 2222: Check if host firewall allows port 2222 and verify SSH_PORT setting in Gitea configuration
  • [!]Redis connection errors in Gitea logs: Confirm Redis container is running and accessible on gitea_net network
  • [!]Actions runner out of disk space: Configure Docker cleanup policies and monitor /var/lib/docker usage on host system

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