Forgejo Actions: Turning Self‑Hosted CI into a One‑Click Feature
A team that wants continuous integration without a SaaS or Jenkins can use Forgejo Actions. This post explains how workflows live in .forgejo/workflows, how act_runner daemons pick up jobs via labels, shows a full example, and weighs the trade‑offs of owning the runner infrastructure.
26 Jan 2026, 12:24 UTC

Why Forgejo Actions? The concrete problem
Many teams still rely on external CI providers or on a monolithic Jenkins cluster. The cost, vendor lock‑in, or maintenance overhead can outweigh the benefits. Forgejo, a self‑hosted Git forge forked from Gitea, ships a built‑in CI engine called Forgejo Actions. It follows the GitHub Actions YAML syntax, so teams familiar with that ecosystem can drop into a self‑hosted workflow with minimal friction.
How the system works
Forgejo Actions is a two‑part architecture:
- Workflow files live in
.forgejo/workflows/*.ymlinside the repository. The Forgejo server parses them, creates a run, and exposes the status in the repository’s Actions tab. - act_runner daemons run on your infrastructure. Each daemon registers with the Forgejo instance using a token from the admin area. The daemon pulls queued jobs, runs them inside a container, and streams logs back to the server.
Label routing is the key to matching jobs with the right runner. In the act_runner configuration you declare a set of labels that map to container images. A job’s runs-on list must contain at least one label that the runner advertises. This keeps the workflow definition independent of the underlying host or image.
Setting up a runner
Below is a minimal configuration for a Linux host that will run jobs on a Docker image. Adjust the placeholders for your environment.
# /etc/forgejo/act_runner.yml
# Register this daemon with the Forgejo instance
instance_url: "https://forgejo.example.com"
token: "YOUR_RUNNER_TOKEN"
# The labels this runner advertises
labels:
- "docker"
# Mapping of labels to container images
image:
docker: "docker:stable"
# Optional: define the working directory inside the container
workdir: "/workspace"
# Optional: environment variables passed to all jobs
env:
LANG: "en_US.UTF-8"
# Runtime options
# (use "docker run" flags when you need more control)
After editing the file, start the daemon:
# As root or a user with sufficient permissions
systemctl enable --now act_runner
Verify that the runner appears under Admin → Actions → Runners in the Forgejo UI. If not, check the logs in /var/log/act_runner.log and ensure the token matches the one issued by the admin panel.
Workflow example: a push‑triggered test run
Place the following file in .forgejo/workflows/test.yml of your repository. The workflow checks out the code, installs dependencies, and runs tests. It’s intentionally simple to demonstrate the end‑to‑end flow.
name: CI
on:
push:
branches: [main]
jobs:
test:
runs-on: [docker]
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Install deps
run: |
npm install
- name: Run tests
run: |
npm test
env:
CI: "true"
Commit and push this file. After a few seconds you’ll see a new run in the repository’s Actions tab. Clicking the run shows a log stream that originates from the act_runner daemon. The log output will look similar to what you’d see from a GitHub Actions run, but it is generated entirely on your own infrastructure.
Practical considerations & trade‑offs
- Infrastructure ownership: You must maintain the host machines, install Docker, and keep act_runner up to date. The advantage is full control over the environment.
- Isolation: Each job runs inside a fresh container, but the host remains shared. If you need stronger isolation (e.g., for security‑sensitive jobs), consider running each runner on a dedicated VM or using container‑d isolation features.
- Scaling: To handle many concurrent jobs, add more runners and diversify labels. The job scheduler will distribute jobs based on label matches.
- Secrets & permissions: Repository and organization secrets are available as
${{ secrets.NAME }}in the workflow. Ensure the runner’s network has access to any external services required by your jobs. - Fork protection: By default, Forgejo may allow workflows from forks to trigger runs. Review the instance’s Actions settings and consider disabling “Allow workflows from forks” if you want tighter control.
- Feature parity: Forgejo Actions implements a pragmatic subset of GitHub Actions. Complex matrix strategies, composite actions, or actions that rely on GitHub‑specific APIs may fail. Test your workflows against the version of Forgejo you run.
Getting started in practice
- Spin up a spare server or a VM.
- Install Docker and the act_runner daemon as shown above.
- Register the runner in the Forgejo admin UI.
- Create a small test repository, add the
.forgejo/workflows/test.ymlfile, and push to themainbranch. - Watch the Actions tab for the run, verify logs, and confirm that the job completes successfully.
- Once comfortable, experiment with matrix builds, artifacts, and multiple runner pools.
Remember to monitor runner health: check the daemon logs, set up alerting on failed jobs, and keep the host’s OS and Docker up to date. With these steps, your team can run continuous integration next to your code without a third‑party SaaS or a sprawling Jenkins installation.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.