Due to the security issues and lack of maintainers, Codeberg does not offer CI itself. I will try to setup Forgejo Actions using my Raspberry Pi to run.

Initial setup

Refer to the official document Forgejo Runner installation guide. Most of the self-hosted on my Raspberry Pi is running in Docker, using Docker Compose.

First prepare a data directory with non-root permissions (in this case, we pick 1001:1001):

#!/usr/bin/env bash
 
set -e
 
mkdir -p data/.cache
 
chown -R 1001:1001 data
chmod 775 data/.cache
chmod g+s data/.cache

Execute the script bash setup.sh, and then create a docker-compose.yml:

version: '3.8'
 
services:
  docker-in-docker:
    image: docker:dind
    container_name: 'docker_dind'
    privileged: 'true' # Required for DinD to function properly
    command: ['dockerd', '-H', 'tcp://0.0.0.0:2375', '--tls=false']
    restart: 'unless-stopped'
 
  runner:
    image: 'data.forgejo.org/forgejo/runner:11'
    links:
      - docker-in-docker
    depends_on:
      docker-in-docker:
        condition: service_started
    container_name: 'runner'
    environment:
      DOCKER_HOST: tcp://docker-in-docker:2375
    # User without root privileges, but with access to `./data`.
    user: 1001:1001
    volumes:
      - ./data:/data
    restart: 'unless-stopped'
 
    command: '/bin/sh -c "while : ; do sleep 1 ; done ;"'

This setup creates 2 containers:

  • docker-in-docker: A Docker daemon running inside a container

    • privileged: true gives the container elevated permissions needed for Docker operations
    • Listens on TCP port 2375 without TLS (simplified setup)
  • runner: The Forgejo Actions runner that connects to the Docker daemon

    • The runner connects to the Docker daemon via DOCKER_HOST
    • Uses user ID 1001 for better security (non-root)
    • Persists data in ./data directory on your host

The purpose of this is to help create isolated job containers for each workflow execution.

Now run docker compose up -d and then enter the container environment with docker exec -it runner /bin/sh

In this shell, run the forgejo-runner register command, but there are some details that need clarification. For the Forgejo instance URL, enter https://codeberg.org/, and the runner token can be acquired from Codeberg's User settings -> Actions -> Runners -> Create new runner.

$ forgejo-runner register
INFO Registering runner, arch=arm64, os=linux, version=v9.0.3.
WARN Runner in user-mode.
INFO Enter the Forgejo instance URL (for example, https://next.forgejo.org/):
https://codeberg.org/
 
INFO Enter the runner token:
6om01axzegBu98YCpsFtda4Go2DuJe7BEepzz2F3HY
 
INFO Enter the runner name (if set empty, use hostname: runner-host):
my-forgejo-runner
 
INFO Enter the runner labels, leave blank to use the default labels (comma-separated, for example, ubuntu-20.04:docker://node:20-bookworm,ubuntu-18.04:docker://node:20-bookworm):
 
INFO Registering runner, name=my-forgejo-runner, instance=https://code.forgejo.org/, labels=[docker:docker://node:20-bookworm].
DEBU Successfully pinged the Forgejo instance server
INFO Runner registered successfully.

This process will create a .runner file under data/ directory:

{
  "WARNING": "This file is automatically generated by act-runner. Do not edit it manually unless you know what you are doing. Removing this file will cause act runner to re-register as a new runner.",
  "id": 42,
  "uuid": "d2ax6368-9c20-4dy0-9a5a-e09c53854zb5",
  "name": "my-forgejo-runner",
  "token": "864e6019009e1635d98adf3935b305d32494d42a",
  "address": "https://codeberg.org/",
  "labels": ["docker:docker://node:20-bookworm"]
}

After that is done, take the service down again with docker compose down and modify the command inside the docker-compose.yml to:

...
command: '/bin/sh -c "sleep 5; forgejo-runner daemon"'

Here, the sleep allows the docker-in-docker service to start up before the forgejo-runner daemon is started.

Then generate a config file within the Docker shell:forgejo-runner generate-config > /data/config.yml

Enable Actions: In your Codeberg repository settings, go to Units > Overview and ensure Actions is enabled.