Local Git and Runners
Self-Hosted CI/CD with Gitea Actions: A Docker Compose Guide
This guide walks you through setting up a complete, self-hosted CI/CD environment using Gitea, Gitea Actions Runner, and PostgreSQL, all managed with Docker Compose. The actual documentation for Gitea is very comprehensive but doing so a lot gets lost in the weeds. thisis the config i have used to make stuff ACTUALLY work.
Prerequisites
Before you begin, make sure you have the following installed on your system:
- Docker: Follow the official Docker installation guide for your operating system (https://docs.docker.com/engine/install/ubuntu/ - you provided the Ubuntu link, so I’m assuming that’s your OS. Adapt if necessary).
- Basic Command-Line Familiarity
Make sure the docker daemon is running and you have done the post install steps listed on the docker website to change folder and group owners. It makes things alot easier
Step 1: Project Setup
- Create a Project Directory:
1 2
mkdir gitea-actions-setup cd gitea-actions-setup
- Create Directories for Data Persistence:
1
mkdir -p ./gitea ./postgres ./runners
These directories will store Gitea’s data, the PostgreSQL database, and runner configurations, respectively. This ensures your data survives container restarts and upgrades.
Step 2: Create the docker-compose.yml File
Create a file named docker-compose.yml in your gitea-actions-setup directory and paste the following configuration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
networks:
gitea_net: # Define a custom network for our services
services:
server:
image: docker.io/gitea/gitea:nightly # Use the nightly build (consider 'latest' for production)
container_name: gitea
environment:
- USER_UID=1000 # Important for file permissions
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432 # Connect to the 'db' service
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=gitea
restart: always
networks:
- gitea_net
volumes:
- ./gitea:/data # Persist Gitea data
- /etc/timezone:/etc/timezone:ro # Set timezone (important for logs)
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000" # Expose Gitea's web interface
- "222:22" # Expose SSH port (optional, but useful)
depends_on:
- db
db:
image: docker.io/library/postgres:14 # Use PostgreSQL 14
restart: always
environment:
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=gitea
- POSTGRES_DB=gitea
networks:
- gitea_net
volumes:
- ./postgres:/var/lib/postgresql/data # Persist PostgreSQL data
runner:
image: docker.io/gitea/act_runner:nightly
environment:
CONFIG_FILE: /config.yaml
GITEA_INSTANCE_URL: "http://gitea:3000" # Use the 'gitea' service name
GITEA_RUNNER_REGISTRATION_TOKEN: "registration token" # REPLACE THIS!
GITEA_RUNNER_NAME: "runner"
networks:
- gitea_net
depends_on:
- db
- server
volumes:
- ./config.yaml:/config.yaml # Mount the runner config
- ./runners:/data #persist runner file
- /var/run/docker.sock:/var/run/docker.sock # Allow Docker-in-Docker
gitea-actions-setup directory:
here is the config.yaml
YOU NEED THE CONFIG FILE. every thing on the internet says you dont but if you dont specify the network you wont be able to pull repos from gitea. USE THE CONFIG FILE.
log:
level: info # You can change this to 'debug' for more verbose logs
runner:
file: .runner
capacity: 1 # Number of concurrent jobs
envs: {} # Add any global environment variables here
env_file: "" # Or specify a .env file
timeout: 3h
shutdown_timeout: 0s
insecure: false
fetch_timeout: 5s
fetch_interval: 2s
labels:
- "ubuntu-latest:docker://gitea/runner-images:ubuntu-latest" # Keep at least one label
- "ubuntu-22.04:docker://gitea/runner-images:ubuntu-22.04"
- "ubuntu-20.04:docker://gitea/runner-images:ubuntu-20.04"
cache:
enabled: true
dir: ""
host: ""
port: 0
external_server: ""
container:
network: "gitea_gitea_net" # VERY IMPORTANT: Use the network name from docker-compose.yml
privileged: false # Set to 'true' if you need Docker-in-Docker and privileged mode.
options: {}
workdir_parent: ""
valid_volumes:
- '**' # Allow mounting any volume (for simplicity, but be cautious in production)
docker_host: "" # Leave empty to automatically detect the Docker host
force_pull: true
force_rebuild: false
host:
workdir_parent: "" # use default
Important Changes:
container.network: Ensure this matches the networks key in your docker-compose.yml file (e.g., gitea_gitea_net). This is critical for the runner to communicate with Gitea. you might look at that and think “oh thats wrong” it is not this is how docker defaults names networks.
docker compose up -d
Test Workflow
in your repo make a directory called.gitea/workflows in there make a file called demo.yaml
name: Test Workflow
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest # Use a label matching your runner's configuration
steps:
- uses: actions/checkout@v3
- run: echo "Hello from Gitea Actions!"
- run: uname -a
when you push the file to gitea it should now run the actions. From here on documentation is your friend have a look at some examples of what you can do with this
Notes
- Dont use the SSH stuff if your using docker it is a pain in the butt to get the keys configured and you would only be locally hosting this so using http isnt a big deal
