Forgejo Runner configurations, running with root-less Podman
Find a file
2025-08-23 11:57:24 -04:00
quadlets Initial commit 2025-08-23 10:48:41 -04:00
.gitignore Initial commit 2025-08-23 10:48:41 -04:00
README.md Update README.md 2025-08-23 11:57:24 -04:00

Forgejo Runner Quadlets

My configurations for running a Forgejo Runner with root-less Podman Quadlets. This configuration enables Actions to build container images using buildah.

Set-up

Install podman:

sudo apt-get update && sudo apt-get install -y podman fuse-overlayfs
sudo modprobe fuse

Create the data directories (/data) for persisting Forgejo runner configurations and cache:

sudo mkdir -p /data/.cache /data/.config
sudo touch /data/.runner

sudo chown -R $UID:$UID /data
sudo chmod -R 700 /data

Clone this repository:

git clone https://git.poire.dev/aramperes/forgejo-runner-quadlet.git

For the first-time set-up, you will need to stop the Forgejo Runner from starting up so you can configure the token with your Forgejo server.

# vim forgejo-runner-quadlet/quadlets/forgejo-runner.container

- Exec=/bin/sh -c "sleep 5; forgejo-runner daemon -c .config/config.yml"
- # Exec=/bin/sh -c "while : ; do sleep 1 ; done ;"
+ # Exec=/bin/sh -c "sleep 5; forgejo-runner daemon -c .config/config.yml"
+ Exec=/bin/sh -c "while : ; do sleep 1 ; done ;"

Create the directory for the quadlets. SystemD will be looking for the quadlets here, and we can use symbolic links to keep them in sync with this repo.

mkdir -p $HOME/.config/containers/systemd

for f in forgejo-runner-quadlet/quadlets/*; do ln -s $(realpath $f) $HOME/.config/containers/systemd/$(basename $f); done

Confirm:

ls -l $HOME/.config/containers/systemd/

# forgejo-runner.container -> /home/user/forgejo-runner-quadlet/quadlets/forgejo-runner.container
# podman-runtime.container -> /home/user/forgejo-runner-quadlet/quadlets/podman-runtime.container
# runner.network -> /home/user/forgejo-runner-quadlet/quadlets/runner.network

Run SystemD generator dry-run to validate the quadlets:

/usr/lib/systemd/system-generators/podman-system-generator --user --dryrun && echo 'Validated!'

Reload SystemD daemon and start the Runner. This will automatically create the Podman Runtime container and the bridge network.

systemctl --user daemon-reload
systemctl --user start forgejo-runner.service

Confirm the containers are running. This might take a few seconds.

podman ps

# CONTAINER ID  IMAGE                              COMMAND               CREATED         STATUS         PORTS       NAMES
# b2e57b02edea  quay.io/podman/stable:latest       podman system ser...  6 minutes ago   Up 6 minutes               systemd-podman-runtime
# 7521cab26b80  data.forgejo.org/forgejo/runner:9  /bin/sh -c while ...  14 seconds ago  Up 15 seconds              systemd-forgejo-runner

To enable automatic start-up on boot, your user needs to have "lingering" enabled in SystemD:

sudo loginctl enable-linger $USER

Register the Runner

On your Forgejo server's Site administration page, go to Action then Runners. Click Create a new runner and copy the token.

Back on your Forgejo Runner host, run:

podman exec -it systemd-forgejo-runner /bin/sh

# Run inside the container and follow the instructions:
# (I recommend setting the label 'docker')
forgejo-runner register

# Confirm the .runner file has been written to:
cat .runner

# Write out default configurations:
forgejo-runner generate-config > .config/config.yml

exit

For building containers, you will need to tweak the runner's configurations to create privileged containers and avoid creating a nested network stack. Don't worry, this is still sandboxed inside the podman-runtime container.

# sudo vim /data/.config/config.yml

container:
-  network: ""
+  network: host
-  privileged: false
+  privileged: true

Revert the runner's quadlet to launch the daemon:

# vim forgejo-runner-quadlet/quadlets/forgejo-runner.container

- # Exec=/bin/sh -c "sleep 5; forgejo-runner daemon -c .config/config.yml"
- Exec=/bin/sh -c "while : ; do sleep 1 ; done ;"
+ Exec=/bin/sh -c "sleep 5; forgejo-runner daemon -c .config/config.yml"
+ # Exec=/bin/sh -c "while : ; do sleep 1 ; done ;"

Reload the quadlet and restart it:

systemctl --user daemon-reload
systemctl --user restart forgejo-runner.service

Now, in the Forgejo server Runners page, you should see your new runner as "Idle".

Example Action: build and push an image

Note, secrets.PACKAGE_TOKEN is a Forgejo Personal Access Token (PAT) with write-permissions to the Packages function. This has to be created manually because the automatic token FORGEJO_TOKEN does not have this permission. Track this feature request.

# .forgejo/workflows/image.yaml

on:
  push:
    branches:
      - master
jobs:
  build:
    runs-on: docker
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Extract the tag
        id: extract_tag
        run: echo "::set-output name=tag::$(echo ${{ forge.sha }} | cut -c1-10)"
      - name: Install Buildah and Podman
        run: apt-get update && apt-get install -y buildah podman
      - name: Buildah Build
        id: build-image
        uses: redhat-actions/buildah-build@v2
        with:
          image: aramperes/imagename
          tags: ${{ steps.extract_tag.outputs.tag }}
          context: .
          containerfiles: ./Dockerfile
      - name: Push to Registry
        uses: https://github.com/redhat-actions/push-to-registry@v2
        with:
          image: ${{ steps.build-image.outputs.image }}
          tags: ${{ steps.build-image.outputs.tags }}
          registry: git.poire.dev
          username: ${{ env.FORGEJO_ACTOR }}
          password: ${{ secrets.PACKAGE_TOKEN }}

Making Changes

If you would like to modify the quadlet configurations, you will need to run these commands to validate, apply, and restart:

/usr/lib/systemd/system-generators/podman-system-generator --user --dryrun && echo 'Validated!'
systemctl --user daemon-reload
systemctl --user restart podman-runtime.service
systemctl --user restart forgejo-runner.service