This content originally appeared on dbushell.com (blog) and was authored by dbushell.com (blog)
Did I mention GitHub is dead to me? I’m moving my code to a self-hosted Forgejo instance at git.dbushell.com. Today I added Actions.
Forgejo Actions are a thing like GitHub Actions for “continuous integration”. Love a bit of CI! Forgejo Actions don’t work out of the box. You need to provide a “runner” which is a machine where all the action happens inside a containerised environment.
Requirements
I’ve had a private Forgejo instance for a while but Actions looked too daunting to setup until now. Robey Pointer’s guide to “Setting up a CI server for Forgejo” inspired me to tackle this challenge. Reading this guide helped me understand how it works.
- Forgejo is the git/web server
forgejo-runneris a daemon that watches Forgejo for workflows- The daemon orchestrates CI jobs inside containers
- The daemon reports back to Forgejo and cleans up its own mess
It’s strongly recommended not to install forgejo-runner on the same machine as the Forgejo instance for security and performance. My instance is running on a Raspberry Pi 5 which isn’t known for its CI horsepower.
After I understood the requirements I realised I didn’t want to install forgejo-runner on bare metal. I decided to install it in Docker on a Proxmox VM alongside the Git LFS (large file storage) server I built. I found another guide by Linus Groh which is a little outdated but the general idea still works.
Docker Everything
My bootstrap docker compose file for forgejo-runner began like so:
services:
forgejo_runner:
container_name: forgejo_runner
image: code.forgejo.org/forgejo/runner:9
command: ["tail", "-f", "/dev/null"]
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/dataNotice the initial command is a no-op that keeps the container alive. The Forgejo runner daemon requires manual configuration before it’ll run.
First I spun up the container and entered the shell.
docker compose up -d
docker exec -it forgejo_runner shThen I ran two commands (probably in the wrong order, but it didn’t matter).
Inside the container I first ran the register command.
forgejo-runner registerThis prompted me for my Forgejo URL https://git.dbushell.com and a secret token. The token can be generated under Admin settings > Actions > Runners and clicking the “Create new runner” button.

This process created a /data/.runner JSON file inside the mounted data volume.
Next I generated the default config.
forgejo-runner generate-config > config.ymlThis command creates /data/config.yml inside the data volume.
Then I exited the container shell with exit.
I then edited my docker compose file to mount the host Docker socket. This allows the runner daemon to orchestrate its own containers where the magic CI happens.
Finally, after blindly following a Stack Overflow tip, I added the docker group ID from the host to my compose file. I then changed the command to launch the forgejo-runner daemon and restarted the container.
services:
forgejo_runner:
container_name: forgejo_runner
image: code.forgejo.org/forgejo/runner:9
command: "/bin/forgejo-runner --config /data/config.yml daemon"
restart: always
group_add:
- 995
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/dataI checked the container logs and it looked good.
docker logs -f forgejo_runner
time="2025-08-15T13:04:30Z" level=info msg="Starting runner daemon"
time="2025-08-15T13:04:30Z" level=info msg="runner: git.dbushell.com, with version: v9.0.3, with labels: [docker], declared successfully"
time="2025-08-15T13:04:30Z" level=info msg="[poller 0] launched"I checked my Forgejo instance and it looked good.

Will it actually work? There’s only one way to find out…
Will it Build?
I setup a demo Vite project using npm create vite@latest.
Forgejo actions are stored in .forgejo/workflows where I created test.yml.
name: Build
on:
push:
branches:
- main
jobs:
build:
runs-on: docker
steps:
- name: Check repository
uses: actions/checkout@v4
- name: Install dependencies
run: npm install
- name: Run Node
run: npm run buildThe runs-on: docker references the runner name.

Remarkable! I just one-shot a successful CI action! That has never happened before. Ignore the fact that the initial setup took negative 20 seconds. Just a minor time synchronisation issue. I see no red crosses so it counts as a win.
Wrap-up
Groh’s guide mentions caching which I didn’t configure and I don’t really understand.
I moved my Patchwork repo to Forgejo with a more complicated Deno build action and it successfully deployed to Cloudflare Pages. The final boss is my ZSP project which requires signed releases and stored artefacts.
However, I’m on a winning streak, it’s 4pm and over 33°C in my office. Ciao!
This content originally appeared on dbushell.com (blog) and was authored by dbushell.com (blog)
dbushell.com (blog) | Sciencx (2025-08-15T10:00:00+00:00) Self-hosted Forgejo Actions Runner. Retrieved from https://www.scien.cx/2025/08/15/self-hosted-forgejo-actions-runner/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.