Running Your Own Forgejo Runner on Codeberg: A Dev's Guide to Self-Hosted CI/CD

Running Your Own Forgejo Runner on Codeberg: A Dev's Guide to Self-Hosted CI/CD

May 22, 2026 codeberg forgejo ci-cd docker self-hosting github-alternative devops automation

Why Self-Host Your CI/CD Runner?

If you've been exploring Codeberg as a GitHub alternative, you've probably discovered that it's an excellent choice. Built on Forgejo and maintained by a non-profit organization, Codeberg offers developers a privacy-respecting, community-driven platform for version control. However, like any resource-conscious non-profit, Codeberg's shared runner infrastructure has limitations.

The solution? Host your own Forgejo runner. The beautiful part is that runners initiate connections outbound to Codeberg, so they work perfectly on a VPS, your home server, or even your laptop. No port forwarding required.

Understanding the Architecture

Before we dive into the technical setup, let's understand what we're building. A Forgejo runner is essentially a worker that listens for jobs from Codeberg and executes them. For security reasons, we'll isolate the runner from your host system using Docker-in-Docker (DinD). This means the runner spins up containerized environments without touching your host's Docker daemon—a critical safety feature.

Setting Up Docker Compose

The core of our setup is a simple two-container system:

version: '3.8'

services:
  docker-in-docker:
    image: docker:dind
    container_name: 'docker_dind'
    privileged: 'true'
    command: ['dockerd', '-H', 'tcp://0.0.0.0:2375', '--tls=false']
    restart: 'unless-stopped'

  runner:
    image: 'data.forgejo.org/forgejo/runner:12'
    links:
      - docker-in-docker
    depends_on:
      docker-in-docker:
        condition: service_started
    container_name: 'runner'
    environment:
      DOCKER_HOST: tcp://docker-in-docker:2375
    user: 1001:1001
    volumes:
      - ./data:/data
    restart: 'unless-stopped'
    command: 'forgejo-runner daemon --config runner-config.yml'

This setup creates two containers: one provides an isolated Docker environment, and the other is your Forgejo runner that communicates with it.

Preparing Your System

Before starting the containers, you need to set up the directory structure with proper permissions. Navigate to where you want to store your runner configuration (we recommend /opt/forgejo-runner) and run:

cd /opt/forgejo-runner
mkdir -p data/.cache
chown -R 1001:1001 data
chmod 775 data/.cache
chmod g+s data/.cache

Next, generate the default configuration file:

sudo sh -c 'docker run --rm data.forgejo.org/forgejo/runner:12 forgejo-runner generate-config > data/runner-config.yml'

This creates a well-documented configuration file that you can customize.

Registering Your Runner with Codeberg

Now comes the integration piece. Head to your Codeberg account (or organization) settings and navigate to Actions → Runners. Click "Create new runner," give it a memorable name, and optionally add a description.

Codeberg will generate a UUID and Token—write these down immediately. These credentials allow your runner to authenticate with Codeberg's infrastructure. You won't see them again without a reset.

Here's the clever part: Codeberg displays the exact YAML snippet you need for configuration, so you don't have to manually write it.

Configuring Labels and Capacity

Open your runner-config.yml and make two important edits:

1. Update the Server Configuration: Add the authentication credentials Codeberg provided in the server section.

2. Configure Labels: Labels are how workflows discover your runner. They use a three-part syntax: [Workflow Label]:[Execution Method]://[Environment]

For example:

labels: 
  - 'ubuntu-latest:docker://node:20-bookworm'
  - 'ubuntu-22.04:docker://node:20-bookworm'
  - 'ubuntu-24.04:docker://node:22-bookworm'

When a workflow specifies runs-on: ubuntu-latest, Codeberg matches it to a runner with that label. This is your matchmaking system between jobs and runners.

Optional: Adjust Capacity By default, runners handle one job at a time. If you have resources, increase the capacity setting to run multiple jobs simultaneously—we recommend starting at 4 for most setups.

Going Live

Once your configuration is complete, start the runner:

docker-compose up -d

Monitor the logs to confirm it's connecting successfully:

docker-compose logs -f runner

You should see confirmation that your runner has registered with Codeberg. After that, any workflow in your repositories that specifies your runner's labels will be delegated to your self-hosted infrastructure.

Performance Wins You'll Notice

By self-hosting your runner, you'll experience:

  • Faster job execution with no queue delays
  • Full control over your CI/CD environment and dependencies
  • Cost savings while supporting Codeberg's non-profit mission
  • Privacy keeping your workflow logs on your own infrastructure
  • Flexibility to scale with your needs

Final Thoughts

Setting up a self-hosted Forgejo runner is one of those infrastructure decisions that pays dividends immediately. It's straightforward enough for a Friday afternoon project, yet powerful enough to scale with your development team. Plus, by reducing load on Codeberg's shared runners, you're directly supporting the open-source ecosystem.

If you're already using Codeberg, this is a natural next step. If you're still on GitHub and considering alternatives, reliable CI/CD is often the deciding factor—and now you know you can take full control of it.

Read in other languages:

RU BG EL CS UZ TR SV FI RO PT PL NB NL HU IT FR ES DE DA ZH-HANS