Changes PickPlatform to return empty string when no matching label is found, causing the job to fail with a clear error rather than silently falling back to ubuntu-latest. This prevents jobs from running in incorrect environments when runner labels are edited in Gitea admin UI after registration. Adds comprehensive test coverage for label matching scenarios including exact matches, no matches, empty labels, and multiple runsOn values.
GitCaddy Runner
A Gitea Actions runner with enhanced capability detection and reporting for AI-friendly workflow generation.
GitCaddy Runner is a hard fork of Gitea's act_runner, rebranded and enhanced with automated capability detection to enable AI tools to generate compatible workflows based on available resources.
Features
- Automated Capability Detection: Automatically identifies OS, architecture, installed tools, and available resources
- Concurrent Job Execution: Configure runner capacity to handle multiple jobs simultaneously
- Docker Support: Full support for Docker and Docker Compose workflows
- Xcode Integration: Detects Xcode installations, SDKs, and simulators on macOS
- Tool Detection: Identifies installed tools (Node.js, Python, .NET, Go, Ruby, Swift, etc.)
- AI-Friendly API: Exposes capabilities through Gitea's API for automated workflow generation
- Cache Support: Built-in workflow cache support for faster builds
Installation
Pre-built Binaries
Download the latest release for your platform from the releases page:
macOS:
# Apple Silicon (M1/M2/M3/M4)
curl -L -o gitcaddy-runner https://git.marketally.com/gitcaddy/gitcaddy-runner/releases/download/v1.0.0/gitcaddy-runner-1.0.0-darwin-arm64
chmod +x gitcaddy-runner
# Intel
curl -L -o gitcaddy-runner https://git.marketally.com/gitcaddy/gitcaddy-runner/releases/download/v1.0.0/gitcaddy-runner-1.0.0-darwin-amd64
chmod +x gitcaddy-runner
Linux:
# x86_64
curl -L -o gitcaddy-runner https://git.marketally.com/gitcaddy/gitcaddy-runner/releases/download/v1.0.0/gitcaddy-runner-1.0.0-linux-amd64
chmod +x gitcaddy-runner
# ARM64
curl -L -o gitcaddy-runner https://git.marketally.com/gitcaddy/gitcaddy-runner/releases/download/v1.0.0/gitcaddy-runner-1.0.0-linux-arm64
chmod +x gitcaddy-runner
Windows:
# Download the Windows executable
# https://git.marketally.com/gitcaddy/gitcaddy-runner/releases/download/v1.0.0/gitcaddy-runner-1.0.0-windows-amd64.exe
Build from Source
git clone https://git.marketally.com/gitcaddy/gitcaddy-runner.git
cd gitcaddy-runner
make build
Quick Start
1. Enable Gitea Actions
In your Gitea app.ini:
[actions]
ENABLED = true
2. Generate Configuration
./gitcaddy-runner generate-config > config.yaml
3. Configure the Runner
Edit config.yaml to customize settings. Important configuration options:
log:
level: info
runner:
file: .runner
capacity: 2 # Number of concurrent jobs (default: 1)
timeout: 3h
shutdown_timeout: 3m # Grace period for running jobs on shutdown
insecure: false
fetch_timeout: 5s
fetch_interval: 2s
labels:
- "ubuntu-latest:docker://node:16-bullseye"
- "ubuntu-22.04:docker://node:16-bullseye"
cache:
enabled: true
dir: ""
container:
network: ""
privileged: false
options: ""
valid_volumes: []
docker_host: ""
force_pull: false
host:
workdir_parent: ""
Capacity Configuration
The capacity setting controls how many jobs the runner can execute simultaneously:
- Default: 1 (one job at a time)
- Recommended: 2-4 for multi-core systems
- Considerations:
- Each job consumes CPU, memory, and disk I/O
- iOS/macOS builds are resource-intensive (start with 2)
- Lighter builds (Node.js, Go) can handle higher capacity (4-6)
- Monitor system load and adjust accordingly
Example for different workloads:
# Light builds (web apps, APIs)
runner:
capacity: 4
# Mixed builds
runner:
capacity: 2
# Heavy builds (iOS/macOS, large containers)
runner:
capacity: 1
4. Register the Runner
./gitcaddy-runner register \
--instance https://your-gitea-instance.com \
--token YOUR_REGISTRATION_TOKEN \
--name my-runner \
--labels ubuntu-latest:docker://node:16-bullseye
The registration token can be obtained from:
- Gitea Admin Panel > Actions > Runners
- Or repository Settings > Actions > Runners
5. Start the Runner
Important: Always specify the config file path with -c flag:
./gitcaddy-runner daemon -c config.yaml
Without the -c flag, the runner will use default settings and ignore your config.yaml!
Running as a Service
macOS (launchd)
Create ~/Library/LaunchAgents/com.gitcaddy.runner.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.gitcaddy.runner</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/gitcaddy-runner</string>
<string>daemon</string>
<string>-c</string>
<string>/path/to/config.yaml</string>
</array>
<key>WorkingDirectory</key>
<string>/path/to/runner/directory</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/path/to/runner.log</string>
<key>StandardErrorPath</key>
<string>/path/to/runner.err</string>
</dict>
</plist>
Load the service:
launchctl load ~/Library/LaunchAgents/com.gitcaddy.runner.plist
Linux (systemd)
Create /etc/systemd/system/gitcaddy-runner.service:
[Unit]
Description=GitCaddy Actions Runner
After=network.target
[Service]
Type=simple
User=runner
WorkingDirectory=/home/runner/gitcaddy-runner
ExecStart=/home/runner/gitcaddy-runner/gitcaddy-runner daemon -c /home/runner/gitcaddy-runner/config.yaml
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable gitcaddy-runner
sudo systemctl start gitcaddy-runner
Windows (NSSM or Native Service)
GitCaddy Runner has native Windows service support. When running as a service, it automatically detects the Windows Service Control Manager (SCM) and handles stop/shutdown signals properly.
Option 1: Using NSSM (Recommended)
Install NSSM via Chocolatey:
choco install nssm -y
Create the service:
# Install the service
nssm install GiteaRunnerSvc C:\gitea-runner\gitcaddy-runner.exe daemon --config C:\gitea-runner\config.yaml
# Set working directory
nssm set GiteaRunnerSvc AppDirectory C:\gitea-runner
# Set environment variables
nssm set GiteaRunnerSvc AppEnvironmentExtra HOME=C:\gitea-runner USERPROFILE=C:\gitea-runner
# Configure auto-restart on failure
sc failure GiteaRunnerSvc reset=86400 actions=restart/60000/restart/60000/restart/60000
# Start the service
sc start GiteaRunnerSvc
Option 2: Native sc.exe (requires wrapper)
Create a wrapper batch file C:\gitea-runner\start-runner.bat:
@echo off
set HOME=C:\gitea-runner
set USERPROFILE=C:\gitea-runner
cd /d C:\gitea-runner
C:\gitea-runner\gitcaddy-runner.exe daemon --config C:\gitea-runner\config.yaml
Service Management:
# Check service status
sc query GiteaRunnerSvc
# Start service
sc start GiteaRunnerSvc
# Stop service
sc stop GiteaRunnerSvc
# View service logs (if using NSSM with log rotation)
Get-Content C:\gitea-runner\logs\runner.log -Tail 50
Environment Variables for Windows Services:
| Variable | Description | Example |
|---|---|---|
GITEA_RUNNER_SERVICE_NAME |
Override service name detection | GiteaRunnerSvc |
Capability Detection
GitCaddy Runner automatically detects and reports system capabilities:
Platform Information
- Operating system (darwin, linux, windows)
- Architecture (amd64, arm64)
Container Runtime
- Docker availability and version
- Docker Compose support
- Container runtime type
Development Tools
- Node.js, npm, yarn
- Python, pip
- Go
- .NET
- Ruby
- Rust
- Java
- Swift (macOS)
- Git, Make
macOS-Specific
- Xcode version and build
- Available SDKs (iOS, macOS, tvOS, watchOS, visionOS)
- Simulators
- Code signing tools (codesign, pkgbuild)
- Package managers (Homebrew, CocoaPods, Fastlane)
System Resources
- CPU cores
- Load average
- Disk space and usage
- Network bandwidth
Example Capabilities Output
{
"os": "darwin",
"arch": "arm64",
"capacity": 2,
"docker": true,
"docker_compose": true,
"container_runtime": "docker",
"xcode": {
"version": "15.2",
"build": "15C500b",
"sdks": ["iOS 17.2", "macOS 14.2"]
},
"tools": {
"node": ["20.11"],
"python": ["3.11"],
"swift": ["5.9"]
},
"build_tools": ["fastlane", "cocoapods", "codesign"],
"cpu": {
"num_cpu": 10,
"load_percent": 25.5
},
"disk": {
"free_bytes": 54199226368,
"used_percent": 77.89
}
}
Configuration Reference
Runner Section
| Option | Type | Default | Description |
|---|---|---|---|
capacity |
int | 1 | Maximum concurrent jobs |
timeout |
duration | 3h | Maximum job execution time |
shutdown_timeout |
duration | 3m | Grace period for jobs to complete on shutdown |
insecure |
bool | false | Allow insecure HTTPS |
fetch_timeout |
duration | 5s | Timeout for fetching tasks |
fetch_interval |
duration | 2s | Interval between task fetches |
labels |
[]string | [] | Runner labels for job matching |
env_file |
string | .env | Environment variables file |
Cache Section
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true | Enable cache support |
dir |
string | "" | Cache directory path |
host |
string | "" | External cache server host |
port |
int | 0 | External cache server port |
Container Section
| Option | Type | Default | Description |
|---|---|---|---|
network |
string | "" | Docker network for containers |
privileged |
bool | false | Run containers in privileged mode |
docker_host |
string | "" | Custom Docker host |
force_pull |
bool | false | Always pull latest images |
Troubleshooting
Capacity Not Being Applied
Problem: Runner shows "capacity":1 even though config.yaml has capacity: 2
Solution: Ensure you're using the -c flag when starting the daemon:
# ✅ Correct
./gitcaddy-runner daemon -c /path/to/config.yaml
# ❌ Wrong - uses defaults
./gitcaddy-runner daemon
Verify the config is being loaded:
# Check runner process
ps aux | grep gitcaddy-runner
# Should show: gitcaddy-runner daemon -c /path/to/config.yaml
Docker Not Detected
Problem: Capabilities show "docker":false but Docker is installed
Solution:
-
Ensure Docker Desktop/daemon is running:
docker ps -
Restart the runner after starting Docker:
./gitcaddy-runner daemon -c config.yaml -
Check Docker socket permissions:
ls -l /var/run/docker.sock
Jobs Not Running Concurrently
Problem: Jobs queue instead of running in parallel
Checklist:
- Verify capacity in capabilities output (check runner logs)
- Confirm config.yaml has
capacity > 1 - Ensure runner was started with
-c config.yamlflag - Check system resources aren't maxed out
- Restart runner after config changes
Runner Not Starting
Problem: Runner exits immediately or fails to start
Common causes:
- Invalid config.yaml syntax
.runnerfile missing (runregisterfirst)- Permission issues on working directory
- Invalid Gitea instance URL or token
Debug steps:
# Check config syntax
./gitcaddy-runner generate-config > test-config.yaml
diff config.yaml test-config.yaml
# Test with verbose logging
./gitcaddy-runner daemon -c config.yaml --log-level debug
# Verify registration
cat .runner
Environment Variables
GitCaddy Runner supports environment variable configuration:
| Variable | Description | Example |
|---|---|---|
GITEA_RUNNER_CAPACITY |
Override capacity setting | GITEA_RUNNER_CAPACITY=2 |
GITEA_RUNNER_ENV_FILE |
Custom env file path | GITEA_RUNNER_ENV_FILE=.env.prod |
API Integration
Query runner capabilities via Gitea API:
curl -H "Authorization: token YOUR_TOKEN" \
https://your-gitea.com/api/v1/runners
Use capabilities to generate compatible workflows:
# Example: Use capabilities to select appropriate runner
name: Build
on: [push]
jobs:
build:
runs-on: ${{ capabilities.os == 'darwin' && 'macos-latest' || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v3
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
License
MIT License - see LICENSE for details.
Support
- Issues: https://git.marketally.com/gitcaddy/gitcaddy-runner/issues
- Discussions: https://git.marketally.com/gitcaddy/gitcaddy-runner/discussions
Acknowledgments
GitCaddy Runner is a hard fork of Gitea's act_runner, rebranded and enhanced with automated capability detection and reporting features for AI-friendly workflow generation.
GitCaddy Runner - User Guide
A CI/CD runner for Gitea Actions that executes workflows defined in .gitea/workflows/ directories. This runner connects to your Gitea instance and picks up jobs to execute them in Docker containers or directly on the host machine.
Table of Contents
- Installation
- Getting Started
- Registration
- Running the Daemon
- Configuration
- Labels and Execution Environments
- Docker Deployment
- Kubernetes Deployment
- Common Workflows
- Artifact Upload Helper
- Disk Space Management
- Cache Server
- Troubleshooting
- Best Practices
Installation
From Binary
Download the latest release for your platform:
# Linux AMD64
wget https://git.marketally.com/gitcaddy/gitcaddy-runner/releases/download/v1.0.0/gitcaddy-runner-1.0.0-linux-amd64
# macOS ARM64
wget https://git.marketally.com/gitcaddy/gitcaddy-runner/releases/download/v1.0.0/gitcaddy-runner-1.0.0-darwin-12-arm64
# Windows AMD64
wget https://git.marketally.com/gitcaddy/gitcaddy-runner/releases/download/v1.0.0/gitcaddy-runner-1.0.0-windows-amd64.exe
Make the binary executable:
chmod +x gitcaddy-runner-1.0.0-linux-amd64
sudo mv gitcaddy-runner-1.0.0-linux-amd64 /usr/local/bin/gitcaddy-runner
From Source
Requirements:
- Go 1.23 or later
- Docker (if using Docker-based workflows)
git clone https://git.marketally.com/gitcaddy/gitcaddy-runner.git
cd gitcaddy-runner
make build
sudo mv gitcaddy-runner /usr/local/bin/
Verify Installation
gitcaddy-runner --version
Getting Started
Prerequisites
- Gitea Instance: You need a running Gitea instance (v1.21 or later recommended)
- Docker (optional but recommended): For running workflows in containers
- Registration Token: Obtain from your Gitea instance:
- Go to your repository → Settings → Actions → Runners
- Click "Create new runner" and copy the registration token
Quick Start
- Generate a configuration file:
gitcaddy-runner generate-config > config.yaml
- Register the runner:
gitcaddy-runner register \
--instance https://gitea.example.com \
--token YOUR_REGISTRATION_TOKEN \
--name my-runner \
--labels "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest"
- Start the runner daemon:
gitcaddy-runner daemon
Registration
The runner must be registered with your Gitea instance before it can execute jobs.
Interactive Registration
Run the registration command without flags for an interactive setup:
gitcaddy-runner register
You'll be prompted for:
- Gitea instance URL:
https://gitea.example.com/ - Registration token: Obtained from Gitea UI
- Runner name: Defaults to hostname if left empty
- Labels: Comma-separated list of labels (or press Enter for defaults)
Non-Interactive Registration
For automation or scripts:
gitcaddy-runner register \
--no-interactive \
--instance https://gitea.example.com \
--token YOUR_TOKEN \
--name production-runner \
--labels "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest,ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04"
Ephemeral Runners
For single-use runners (useful for auto-scaling):
gitcaddy-runner register \
--ephemeral \
--instance https://gitea.example.com \
--token YOUR_TOKEN \
--name ephemeral-runner
Ephemeral runners automatically unregister after completing one job.
Registration File
Registration information is stored in .runner (by default). This file contains:
- Runner UUID and token
- Runner name
- Configured labels
- Gitea instance address
Important: Keep this file secure as it contains authentication credentials.
Running the Daemon
Basic Usage
Start the runner daemon to begin accepting jobs:
gitcaddy-runner daemon
With a custom configuration file:
gitcaddy-runner daemon --config /path/to/config.yaml
Run Once Mode
Execute a single job and then exit:
gitcaddy-runner daemon --once
This is useful for:
- Testing runner setup
- Ephemeral runners in auto-scaling environments
- Scheduled job execution
Background Execution
Run as a background service using systemd:
Create /etc/systemd/system/gitcaddy-runner.service:
[Unit]
Description=GitCaddy Runner
After=network.target docker.service
Requires=docker.service
[Service]
Type=simple
User=gitea-runner
WorkingDirectory=/home/gitea-runner
ExecStart=/usr/local/bin/gitcaddy-runner daemon --config /home/gitea-runner/config.yaml
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable gitcaddy-runner
sudo systemctl start gitcaddy-runner
sudo systemctl status gitcaddy-runner
Configuration
Generate Default Configuration
gitcaddy-runner generate-config > config.yaml
Configuration File Structure
log:
level: info # trace, debug, info, warn, error, fatal
runner:
file: .runner # Registration file path
capacity: 1 # Number of concurrent jobs
timeout: 3h # Job timeout
shutdown_timeout: 0s # Graceful shutdown timeout
insecure: false # Skip TLS verification
fetch_timeout: 5s # Job fetch timeout
fetch_interval: 2s # Job fetch interval
github_mirror: '' # Mirror for GitHub actions
envs: # Extra environment variables
NODE_ENV: production
env_file: .env # Load environment from file
labels: # Runner labels
- "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest"
cache:
enabled: true
dir: "" # Cache directory (default: $HOME/.cache/actcache)
host: "" # Cache server host (auto-detected)
port: 0 # Cache server port (0 = random)
external_server: "" # External cache server URL
container:
network: "" # Docker network (empty = auto-create)
privileged: false # Run containers in privileged mode
options: "" # Additional docker run options
workdir_parent: workspace # Parent directory for job workspaces
valid_volumes: # Allowed volume mounts (glob patterns)
- '**'
docker_host: "" # Docker daemon socket (auto-detected)
force_pull: true # Always pull images
force_rebuild: false # Rebuild images even if present
require_docker: false # Require Docker daemon
docker_timeout: 0s # Docker daemon startup timeout
host:
workdir_parent: "" # Host workspace directory (default: $HOME/.cache/act)
Environment Variables
Legacy environment variables (deprecated but still supported):
GITEA_DEBUG: Enable debug loggingGITEA_TRACE: Enable trace loggingGITEA_RUNNER_CAPACITY: Set runner capacityGITEA_RUNNER_FILE: Set registration file pathGITEA_RUNNER_ENVIRON: Set environment variables (comma-separatedkey:value)GITEA_RUNNER_ENV_FILE: Load environment from file
Note: Configuration file settings take precedence over environment variables.
Labels and Execution Environments
Labels determine which jobs a runner can execute and how they run.
Label Format
label-name:schema:argument
- label-name: Identifier used in workflow
runs-on - schema: Execution environment (
dockerorhost) - argument: Additional configuration (e.g., Docker image)
Docker Labels
Run jobs in Docker containers:
labels:
- "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest"
- "ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04"
- "node:docker://node:18"
- "python:docker://python:3.11"
Workflow usage:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: echo "Running in Ubuntu container"
Host Labels
Run jobs directly on the host machine:
labels:
- "self-hosted:host"
- "linux:host"
- "macos:host"
Workflow usage:
jobs:
build:
runs-on: self-hosted
steps:
- uses: actions/checkout@v3
- run: echo "Running on host"
Custom Images
Use custom Docker images:
labels:
- "custom-env:docker://registry.example.com/my-build-env:latest"
Multiple Labels
A runner can have multiple labels to support different job types:
labels:
- "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest"
- "ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04"
- "self-hosted:host"
- "gpu:host"
Docker Deployment
Basic Docker Run
docker run -d \
--name gitcaddy-runner \
-e GITEA_INSTANCE_URL=https://gitea.example.com \
-e GITEA_RUNNER_REGISTRATION_TOKEN=YOUR_TOKEN \
-e GITEA_RUNNER_NAME=docker-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /data/runner:/data \
marketally/gitcaddy-runner:latest
Docker Compose
Create docker-compose.yml:
version: '3'
services:
runner:
image: marketally/gitcaddy-runner:latest
environment:
- GITEA_INSTANCE_URL=https://gitea.example.com
- GITEA_RUNNER_REGISTRATION_TOKEN=YOUR_TOKEN
- GITEA_RUNNER_NAME=compose-runner
- GITEA_RUNNER_LABELS=ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./runner-data:/data
restart: unless-stopped
Start:
docker-compose up -d
Docker-in-Docker (DinD)
For isolated Docker environments:
version: '3'
services:
runner:
image: marketally/gitcaddy-runner:latest-dind
privileged: true
environment:
- GITEA_INSTANCE_URL=https://gitea.example.com
- GITEA_RUNNER_REGISTRATION_TOKEN=YOUR_TOKEN
- DOCKER_TLS_CERTDIR=/certs
volumes:
- runner-data:/data
- docker-certs:/certs
restart: unless-stopped
volumes:
runner-data:
docker-certs:
Rootless Docker
For enhanced security:
version: '3'
services:
runner:
image: marketally/gitcaddy-runner:latest-dind-rootless
environment:
- GITEA_INSTANCE_URL=https://gitea.example.com
- GITEA_RUNNER_REGISTRATION_TOKEN=YOUR_TOKEN
- DOCKER_HOST=unix:///run/user/1000/docker.sock
volumes:
- runner-data:/data
restart: unless-stopped
security_opt:
- seccomp:unconfined
volumes:
runner-data:
Kubernetes Deployment
Using Docker-in-Docker
See examples/kubernetes/dind-docker.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gitcaddy-runner-vol
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Secret
metadata:
name: runner-secret
type: Opaque
data:
token: <base64-encoded-registration-token>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitcaddy-runner
spec:
replicas: 1
selector:
matchLabels:
app: gitcaddy-runner
template:
metadata:
labels:
app: gitcaddy-runner
spec:
volumes:
- name: docker-certs
emptyDir: {}
- name: runner-data
persistentVolumeClaim:
claimName: gitcaddy-runner-vol
containers:
- name: runner
image: git.marketally.com/gitcaddy/gitcaddy-runner:latest
command: ["sh", "-c", "while ! nc -z localhost 2376 </dev/null; do echo 'waiting for docker daemon...'; sleep 5; done; /sbin/tini -- run.sh"]
env:
- name: DOCKER_HOST
value: tcp://localhost:2376
- name: DOCKER_CERT_PATH
value: /certs/client
- name: DOCKER_TLS_VERIFY
value: "1"
- name: GITEA_INSTANCE_URL
value: http://gitcaddy-http.gitcaddy.svc.cluster.local:3000
- name: GITEA_RUNNER_REGISTRATION_TOKEN
valueFrom:
secretKeyRef:
name: runner-secret
key: token
volumeMounts:
- name: docker-certs
mountPath: /certs
- name: runner-data
mountPath: /data
- name: daemon
image: docker:23.0.6-dind
env:
- name: DOCKER_TLS_CERTDIR
value: /certs
securityContext:
privileged: true
volumeMounts:
- name: docker-certs
mountPath: /certs
Apply:
kubectl apply -f dind-docker.yaml
Using Rootless Docker
See examples/kubernetes/rootless-docker.yaml for a more secure rootless setup.
Common Workflows
Basic CI Workflow
Create .gitea/workflows/ci.yaml:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Run tests
run: go test ./...
- name: Build
run: go build -v ./...
Multi-Platform Build
name: Build
on: [push]
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: make build-linux
build-macos:
runs-on: macos
steps:
- uses: actions/checkout@v3
- run: make build-macos
build-windows:
runs-on: windows
steps:
- uses: actions/checkout@v3
- run: make build-windows
Using Caching
name: Build with Cache
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Cache dependencies
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Build
run: go build ./...
Using Artifacts
Important: GitCaddy Runner does not support actions/upload-artifact@v4 or actions/download-artifact@v4. Use v3 instead:
name: Build and Upload
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build
run: make build
- name: Upload artifact
uses: actions/upload-artifact@v3 # Note: v3, not v4
with:
name: my-artifact
path: dist/
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v3 # Note: v3, not v4
with:
name: my-artifact
path: dist/
- name: Deploy
run: ./deploy.sh
Using Services
name: Integration Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- name: Run integration tests
env:
DATABASE_URL: postgres://postgres:postgres@postgres:5432/test
run: go test -tags=integration ./...
Artifact Upload Helper
For large files or unreliable networks, use the built-in upload helper with retry logic:
gitcaddy-upload \
-url https://gitea.example.com/api/v1/repos/owner/repo/releases/1/assets \
-token YOUR_TOKEN \
-file ./large-artifact.zip \
-retries 10
Options:
-url: Upload endpoint URL-token: Authentication token-file: File to upload-retries: Maximum retry attempts (default: 5)
The upload helper automatically:
- Retries on network failures
- Pre-warms connections
- Uses chunked uploads for large files
- Provides detailed progress logging
Disk Space Management
The runner includes automatic disk space monitoring and cleanup capabilities.
Automatic Cleanup
The runner automatically monitors disk usage and triggers cleanup when:
- Disk usage exceeds 85% (warning threshold)
- Disk usage exceeds 95% (critical threshold)
Cleanup targets:
- Old cache directories (>24 hours)
- Old work directories (>48 hours)
- Old artifact staging files (>72 hours)
- System temp files (>24 hours)
- Build tool caches (>7 days): Go, npm, pip, Maven, Gradle, NuGet, Cargo, etc.
Manual Cleanup
Trigger cleanup manually:
gitcaddy-runner cleanup --config config.yaml
Output example:
Cleanup completed: freed 15728640000 bytes, deleted 1234 files in 5.2s
Monitoring Disk Space
The runner reports disk space in its capabilities:
{
"disk": {
"path": "/workspace",
"total_bytes": 107374182400,
"free_bytes": 32212254720,
"used_bytes": 75161927680,
"used_percent": 70.0
}
}
Best Practices
- Monitor disk usage: Check runner logs for disk space warnings
- Configure cleanup: Adjust cache retention in config.yaml
- Use ephemeral runners: For auto-scaling environments
- Separate volumes: Use dedicated volumes for runner data
Cache Server
The runner includes a built-in cache server for actions/cache.
Automatic Cache Server
Enabled by default in the configuration:
cache:
enabled: true
dir: "" # Default: $HOME/.cache/actcache
host: "" # Auto-detected
port: 0 # Random available port
The cache server URL is automatically provided to jobs via ACTIONS_CACHE_URL.
External Cache Server
Use an external cache server:
cache:
enabled: true
external_server: "http://cache.example.com:8080/"
Standalone Cache Server
Run the cache server independently:
gitcaddy-runner cache-server \
--dir /path/to/cache \
--host 0.0.0.0 \
--port 8080
Options:
--dir: Cache storage directory--host: Listen address--port: Listen port
Troubleshooting
Runner Not Picking Up Jobs
Check registration:
cat .runner
Verify the runner appears in Gitea UI (Settings → Actions → Runners).
Check labels: Ensure workflow runs-on matches a runner label.
Check logs:
gitcaddy-runner daemon --config config.yaml
Look for connection errors or authentication issues.
Docker Connection Issues
Error: Cannot ping the docker daemon
Solutions:
- Verify Docker is running:
docker ps
- Check Docker socket path:
ls -la /var/run/docker.sock
- Add user to docker group:
sudo usermod -aG docker $USER
newgrp docker
- Specify Docker host in config:
container:
docker_host: "unix:///var/run/docker.sock"
Timeout Issues
Error: Job timeout after 3 hours
Solution: Increase timeout in config:
runner:
timeout: 6h
Disk Space Issues
Error: No space left on device
Solutions:
- Run manual cleanup:
gitcaddy-runner cleanup
-
Increase disk space or use dedicated volume
-
Configure more aggressive cleanup:
cache:
dir: /path/to/larger/disk
SSL/TLS Certificate Issues
Error: x509: certificate signed by unknown authority
Solution (not recommended for production):
runner:
insecure: true
Better solution: Install proper CA certificates:
sudo cp your-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
Actions Not Found
Error: unable to resolve action
Solution: Configure GitHub mirror if needed:
runner:
github_mirror: 'https://github.mirror.example.com'
Artifact Upload Failures
Error: actions/upload-artifact@v4 not supported
Solution: Use v3 instead:
- uses: actions/upload-artifact@v3 # Not v4
with:
name: my-artifact
path: dist/
For large files with unreliable networks, use the upload helper:
gitcaddy-upload -url $URL -token $TOKEN -file artifact.zip
Best Practices
Security
- Keep registration file secure: The
.runnerfile contains authentication credentials - Use secrets: Never hardcode sensitive data in workflows
- Limit volume mounts: Restrict
valid_volumesin configuration - Use rootless Docker: When possible, use rootless containers
- Regular updates: Keep runner and Docker images up to date
Performance
- Use caching: Cache dependencies to speed up builds
- Parallel jobs: Increase
capacityfor concurrent job execution - Local images: Use
force_pull: falsefor frequently used images - Dedicated volumes: Use fast storage for workspaces and cache
Reliability
- Monitor disk space: Watch for low disk space warnings
- Set appropriate timeouts: Balance between job completion and resource usage
- Use health checks: Monitor runner process and Docker daemon
- Graceful shutdown: Configure
shutdown_timeoutfor clean termination - Log rotation: Implement log rotation to prevent disk fill
Scalability
- Ephemeral runners: Use
--ephemeralfor auto-scaling - Multiple runners: Deploy multiple runners for load distribution
- Label-based routing: Use labels to route jobs to appropriate runners
- Resource limits: Set Docker resource limits in
container.options
Maintenance
- Regular cleanup: Run periodic manual cleanup or rely on automatic cleanup
- Update images: Keep runner images and workflow images current
- Monitor logs: Check logs for warnings and errors
- Test configuration: Validate config changes in staging first
- Backup registration: Keep backup of
.runnerfile
Workflow Best Practices
- Use specific versions: Pin action versions (e.g.,
@v3, not@latest) - Minimize steps: Combine related commands
- Use artifacts wisely: Only upload necessary files
- Clean up: Remove temporary files in workflows
- Test locally: Use
gitcaddy-runner execto test workflows locally
Example Production Setup
# config.yaml
log:
level: info
runner:
capacity: 4
timeout: 2h
shutdown_timeout: 5m
fetch_interval: 2s
labels:
- "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest"
- "ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04"
cache:
enabled: true
dir: /mnt/fast-storage/cache
container:
network: gitea-actions
force_pull: false
workdir_parent: /mnt/fast-storage/workspace
valid_volumes:
- /mnt/artifacts/**
docker_host: unix:///var/run/docker.sock
host:
workdir_parent: /mnt/fast-storage/host-workspace
This configuration provides:
- 4 concurrent jobs
- 2-hour job timeout with 5-minute graceful shutdown
- Fast storage for cache and workspaces
- Restricted volume mounts for security
- Optimized image pulling