Forgejo Actions
- Forgejo Actions administrator guide
- Securing Forgejo Actions Deployments
- Utilizing Docker within Actions
- Forgejo Runner Configuration
- Installation from binary
- Installation with Docker
- Installation from packaging
- Forgejo Runner Registration
Forgejo Actions administrator guide
Forgejo Actions provides continuous integration driven from the files found in the .forgejo/workflows directory of a
repository. Note that Forgejo does not run the jobs, it relies on the Forgejo Runner to do so.
Forgejo Runner
The Forgejo Runner is a program that fetches workflows to run from a Forgejo instance and executes them. It is installed and configured separately from Forgejo.
To get started with Forgejo Runner:
- Install Forgejo Runner
- using a binary installation, or
- using a Docker container, or
- using Linux distribution packaged binaries.
- Register Forgejo Runner to connect it to Forgejo,
- Configure Forgejo Runner to define what jobs it executes and how it operates.
- Choosing labels for the runner is the minimum required configuration.
A single Forgejo Runner can be connected to multiple Forgejo instances, or different users, organizations, or repositories within a single instance. Multiple Forgejo Runner installations can be connected to a single Forgejo instance to distribute jobs over a cluster of available compute resources.
Forgejo Runner performs remote code execution. That poses significant security threats for the host and network that it operates upon. Securing Forgejo Actions Deployments walks through the security considerations and choices that an administrator should be aware of.
To build container images in workflows or run actions that use Docker, additional configuration is required.
Forgejo Settings
Enabling/Disabling
As of Forgejo v1.21, Actions is enabled by default. It can be disabled by adding the following to app.ini:
[actions]
ENABLED = false
Default Actions URL
In a workflow, when uses: does not specify an absolute URL, the
value of DEFAULT_ACTIONS_URL is prepended to it.
[actions]
ENABLED = true
DEFAULT_ACTIONS_URL = https://data.forgejo.org
The actions published at https://data.forgejo.org are:
- known to work with Forgejo Actions
- published under a Free Software license
They can be found in organizations such as actions for general purpose actions, docker for those related to Docker and so on.
When setting DEFAULT_ACTIONS_URL to a Forgejo instance with an open
registration, care must be taken to avoid name conflicts. For
instance if an action has uses: foo/bar@main it will clone and try
to run the action found at DEFAULT_ACTIONS_URL/foo/bar if it exists,
even if it provides something different than what is expected.
Storage
The logs and artifacts are stored in Forgejo. The cache is stored by the runner itself and never sent to Forgejo.
job logs
The logs of each job run is stored by the Forgejo server and
expire after a delay that defaults to 365 days and can be configured as
follows:
[actions]
LOG_RETENTION_DAYS = 365
The location where these files are stored is configured in
the storage.actions_log section of app.ini as explained in in the
storage documentation.
artifacts logs
The artifacts uploaded by a job are stored by the Forgejo server and expire after a delay that defaults to 90 days and can be configured as follows:
[actions]
ARTIFACT_RETENTION_DAYS = 90
The location where these artifacts are stored is configured in
the storage.artifacts section of app.ini as explained in in the
storage documentation.
The admin/monitor/cron administration web interface can be used to
manually trigger the Cleanup actions expired logs and artifacts task
instead of waiting for the scheduled task to happen.
Other runners
It is possible to use other runners instead of Forgejo Runner. As long as they can connect to a Forgejo instance using the same protocol, they will be given tasks to run.
Securing Forgejo Actions Deployments
Remote code execution (RCE) is typically considered one of the most critical security vulnerabilities that can be discovered and exploited in a system. For Forgejo Actions, their entire purpose is to perform remote code execution. Therefore, there are many security considerations in deploying a self-hosted Forgejo Runner.
Every administrator of a self-hosted Forgejo Runner will have a different set of requirements for their deployment to be successful, a different risk environment in which they are deploying their Runner, and a different appetite to accept risk. They will need to make trade-offs to meet their goals and still be able to sleep soundly at night. This documentation will not outline one perfect solution, but it will attempt to inform any administrator about the known risks, and the tools that they can use to manage those risks.
Let's assume that a threat exists, and then we'll explore how we can prevent it. Our threat is: a malicious user, Mallory, is attempting to access a Forgejo instance, and Mallory intends to run customized Forgejo Actions workflows in order to extract confidential data, plant viruses and worms in build artifacts and code, and then crash the system to make it more difficult to recover from their attack.
How can we make Mallory's life really difficult?
Forgejo Access
The first thing Mallory needs to do is access the Forgejo instance that they want to attack, and login to the system.
Many Forgejo instances are deployed publicly in order to enable the distribution of Open Source software. But for some systems, it may be advisable to prevent public access altogether. When deploying a self-hosted Forgejo instance, it can be deployed so that it can only be accessed by users on a local network or a virtual private network (VPN). If deployed like this, Mallory's job is more difficult: they need to escalate into the target network before they can get started. But this configuration isn't appropriate for everyone, and it is just one layer in protecting the instance from Mallory.
After network access is established, managing the risks to a Forgejo Runner system continues with managing the users that have access to the system and the repositories that they can work upon.
service.DISABLE_REGISTRATIONcan be set totruewhich will prevent unexpected users from registering on the Forgejo instance. Mallory will not be able to create their own Forgejo account. Forgejo administrators can create new accounts for users on-demand.repository.MAX_CREATION_LIMITcan be set to0in order to prevent users from creating unexpected repositories on the instance. Mallory won't be able to create new empty repositories from which they may be able to run malicious workflow actions. Forgejo administrators can override this value temporarily when new authorized repositories are created, allowing the system to be usable despite this configuration.- For any value other than
-1and0, be aware thatadmin.DISABLE_REGULAR_ORG_CREATIONmust also be set totrueto prevent users from effectively bypassing this limitation by creating organizations.
- For any value other than
repository.ALLOW_FORK_WITHOUT_MAXIMUM_LIMITcan be set tofalse, which will prevent users from forking existing repositories. Mallory won't be able to fork existing repositories, preventing them from creating pull requests that can trigger workflow runs.
There is no ideal configuration for these values, as changing them affects Forgejo access and security, but limits functionality.
It's easy to assume that a user on a Forgejo instance is an authorized user of that instance. In close-knit instances of known users, administrators should review Admin settings > Identity & access > User accounts, and manually ensure that 2FA is enabled for users. This reduces the risk of Mallory using an old leaked password from a data breach in order to masquerade as an authorized user account.
Runner Registration
We've reviewed a few ways to keep Mallory out of the Forgejo system entirely, but you might choose to invite Mallory into the Forgejo instance. They could be an Open Source contributor in your wonderfully open project, or they could be a new employee in your commercial software organization. We still have ways to minimize risk from the threat that they pose.
Forgejo Runner instances can be registered with Forgejo at four different levels, which will impact the jobs that they will run. By ensuring that the tightest registration is used, the scope of risk from Mallory is reduced:
- Repository - A runner registered with a repository will only run actions defined in that repository.
- Organization & User - A runner registered with an organization or user will only run actions defined in a repository owned by that organization or user.
- Global - A runner registered in Forgejo's site administration can run a job in any repository.
Ephemeral Runners
Administrators of environments where Forgejo Runner instances are created on demand should consider using Forgejo Runner in ephemeral mode. That is especially true when running workflows in the same virtual machine or container without isolation (host) because the runner token is then accessible to the running job. When in ephemeral mode, as opposed to persistent mode, which is the default, Forgejo will assign a runner at most one job before removing it. That means that even if a runner token falls into Mallory's hands, Mallory will not be able to request any other job.
Ephemeral mode can be enabled during runner registration by passing --ephemeral to forgejo-runner register or forgejo forgejo-cli actions register. The HTTP API exposes an ephemeral property, too. Ephemeral mode is enforced by Forgejo and cannot be disabled or ignored.
Forgejo will only remove ephemeral runners after they have received one job and completed it or exceeded a timeout. That means that ephemeral runners can continue asking Forgejo for a job until one is available and potentially linger forever if they do not receive any job. There is currently no way to influence that behavior.
With ephemeral runners, only forgejo-runner one-job can be used. forgejo-runner daemon will exit immediately when it is told by Forgejo to switch to ephemeral mode.
Preventing Arbitrary Workflow Execution
As an administrator of Forgejo Runner, if you wish to ensure that a malicious user like Mallory can never run unauthorized workflows, some of the responsibility will fall onto administrators of the Forgejo instance and the repository administrators. That might also be you, or it might include other colleagues and collaborators. Let's assume that a Forgejo Runner is configured on a specific repository, the tightest registration described in Runner Registration, and explore what we'd need to know to avoid Mallory running an unauthorized workflow.
If Mallory can push directly to the repository, they can run arbitrary workflows. Branch protection rules may appear to be a line of defense (for example: workflows are defined in main and Mallory can only create branches & PRs, not modify main), but Mallory has workarounds for this if they can push to any new branch: they can create a new branch that isn't covered by branch protection, with a workflow with a matching on: push event trigger, and Mallory will be able to run any workflow that they want.
If Mallory does not have direct write access, but can fork the repository and create pull requests, then their first pull request to the repository will require review from a contributor and approval before workflows can execute. After that, any future pull request that Mallory makes will have the workflow executed, including any modifications made in the fork. This can include the creation of a new workflow that has the on: pull_request event, allowing them to introduce workflows that never existed before. The on: pull_request workflows that would be executed in this model do have additional restrictions relative to other workflows -- they have no access to the secrets in the repository.
If the repository has an on: pull_request_target workflow defined in it, then Mallory cannot modify the code that is executed in that workflow unless their pull requests are merged -- the workflow runs with the content of the target branch of the PR, rather than with the content of the pull request. However, these workflows do have access to secrets, and can sometimes make mistakes in interacting with the code in the pull request in a dangerous manner. on: pull_request_target workflows require careful engineering and review to ensure that they do not interact with other code in the repository to introduce new security risks -- these considerations are described in on.pull_request_target.
Runner Configuration
Once Mallory gets access to a repository where they can begin mutating actions workflows, and a runner is configured which will execute those workflows, their access to resources varies considerably based upon the configuration of that runner.
When registering a Forgejo Runner, an administrator will be prompted for runner labels which define the environment that jobs will be executed in. Each of the different label types (docker, lxc, and host) will have different impact on the environment which Mallory would be able to access.
Job Containers with Docker
When Forgejo Runner has labels configured to use a Docker daemon, many of the configuration options in the container section of the Runner's config are security sensitive. The default values are chosen to minimize security risks, but any changes to these values require the administrator to be aware of security side-effects.
Multiple different containers can be used in a single job. These terms will be used to describe each one:
- Job containers: the container image defined by the runner label, or overridden by the
jobs.<job_id>.container.imageproperty, from which future step commands are executed. - Service containers: containers defined in the
jobs.<job_id>.servicessection of the workflow file. - Step containers: containers that are required to
jobs.<job_id>.steps.useswhich use a "Remote container action" or a "Remote action" that is defined withuses: docker.
container.privileged
An administrator can change container.privileged from the default value of false to true. When it is configured as true, each of the job containers, service containers, and step containers will be launched with the --privileged flag:
The --privileged flag allows broad access to the host system through elevated runtime privilege and Linux capabilities.
If container.privileged is configured to true and our attacker Mallory is able to mutate actions workflows that are executed, Mallory will be able to operate as root on the Forgejo Runner machine; all confidential data can be compromised, all data integrity can be compromised, and availability of the service can be disrupted.
Step containers may be defined by remote action workflows, which rely on the integrity of workflows defined in other repositories which may even be hosted in other git forges. If those remote workflows are compromised, running them with privileged access will cause risks to the host that bypass some of the Forgejo access restrictions; Mallory doesn't need to access your Forgejo instance at all if they can mutate a remote workflow that is being executed. Users authoring workflows can minimize the risk of this by using sha-pinned references and reviewing changes when they occur carefully. As an administrator there are no direct controls to prevent this, and it would be inadvisable to use container.privileged set to true if you believe this is a risk and don't take any further isolation action.
container.network
An administrator can change container.network from the default value of "" to "host", "bridge", or a custom network name. The default value "" causes a new isolated network to be created for each new job, and service/step containers are created on the same network to facilitate communication between them.
Changing the network mode to "host" will remove restrictions to allow all containers to communicate freely with each other and the host, without any job isolation. This can be a risk to the confidentiality of data hosted by other services on the host; for example, if a web server is running on 127.0.0.1:3000 that would normally be protected by access controls imposed by a reverse proxy like nginx, Mallory will be able to run job containers that can access this web server directly bypassing those controls. This can risk the availability of workflow jobs as well, by sharing the same network stack; if Mallory starts a workflow which starts a database server that listens to port 5432 which is already in-use in the host, it would probably fail to start due to a network port conflict, but they may be able to claim the port during database server maintenance and affect the availability of the other service.
Changing the network mode to "bridge" will allow unrestricted network access, but scoped only to other docker containers running on the same default "bridge" network. This is more restricted than "host", but allows similar risks if there are confidential services running in docker that are outside the job container.
Changing the network mode to a custom network name will isolate the network of all job containers, but they will not be isolated from each other. In combination with a runner.capacity configured > 1 where multiple workflows will be executing concurrently on the same custom named network, the availability of workflow jobs can be affected as container names can conflict with each other; for example, if two jobs run a service container named pgsql, DNS resolution for the name pgsql will round-robin between the two containers. Confusion between intermittent workflow failures may be the most likely outcome, but our attacker Mallory may be able to access unexpected data through network access to other job, service, or step containers.
container.valid_volumes
The default value of valid_volumes is an empty array []. If an administrator changes this, they will allow a job container or service container to mount the listed volumes when the container is started.
Data on the mounted volumes can be accessed freely by the Forgejo Actions, in both read and read/write mode. It is trivial to execute commands as root within the containers as there is no access control at this level, so no filesystem-level permissions will be effective in blocking access.
If a volume is listed in valid_volumes, the data within it cannot be considered confidential as Mallory would be able to read the data through mutated workflow actions. It also cannot safely be used as an input to critical build jobs without having signature or integrity checks performed against an external validation, as Mallory can mutate this data and may be able to sneak malicious code into build artifacts derived from the volume contents.
container.docker_host
container.docker_host serves two purposes in the runner configuration; both defining where the runner will execute containers, and defining whether that same access to container execution will be shared with the containers. More detailed information on this configuration parameter is provided in the Utilizing Docker within Actions document, along with the security risks associated with it.
Resource Constraints via container.options
Typically, job, service, and step containers are executed with no resource constraints, which allows Mallory to consume as much of the host's CPU and memory as they can. This can affect the ability of the runner server to serve future tasks, to operate continuously, and if the resources are shared with other services, affect the ability to provide those shared services.
The container.options value can be used to define docker container resource limitations such as --memory=1g (limiting the container to 1 GB), or --cpus=2 (limiting the container to 2 CPU cores). When combined with runner.capacity, this allows you to constrain the resources used by job containers and step containers.
However, there is no limit in the Forgejo Runner on the number of simultaneous service containers that a single workflow job can start. There are also no mechanisms available to limit disk or network I/O for any container.
Private Registry Access
If Docker or Podman is logged into OCI image registries that require authentication, then Forgejo Runner can pull images from those registries, too. Workflow authors can specify arbitrary images in workflows using the container: key. Therefore, they can potentially use any image Docker and Podman have access to.
If that is not desired, ensure that Docker and Podman are not logged into any registry that requires authentication.
Job Containers with LXC
When using lxc labels in Forgejo Runner, there are no mechanisms available to restrict resource utilization (memory, CPU, disk & network I/O), which would allow Mallory to consume high levels of resources and affect the availability of the host for other workflow executions, and other shared services.
Execution on Host (host)
When using the host label to allow jobs to run without containers, step commands are executed as the same user as the Forgejo Runner is executed as, and in the same host without any isolation.
Mallory's malicious workflow executions will be able to perform any work on the host that the Forgejo Runner's user would be able to perform. If the Forgejo Runner is executed under an init system like systemd and the systemd unit is hardened and uses a dynamic user, various restrictions can be imposed upon the daemon by the init system. Unfortunately these limitations are outside of the scope of this document as they are specific to the init system being used. However, specific risks to note are:
- The host's network stack will be used by any executed commands. This can risk the confidentiality of other services on the host; for example, if a web server is running on
localhost:3000that would normally be protected by access controls imposed by a reverse proxy like nginx, Mallory will be able to run job containers that can access this web server directly bypassing those controls. This can risk the availability of workflow jobs as well by sharing the same network stack; if Mallory starts a workflow which starts a database server that listens to port5432which is already in-use in the host, it would probably fail to start due to a network port conflict, but they may be able to claim the port during database server maintenance and affect the availability of the other service. - There are no mechanisms within Forgejo Runner to restrict resource utilization (memory, CPU, disk & network I/O), which would allow Mallory to consume high levels of resources and affect the availability of the host for other workflow executions, and other shared services. Hardening with the init system should be able to provide some restrictions if needed.
- The Forgejo Runner necessarily needs to read the runner's
.runnerstate file. In the host execution mode, a workflow job would also be able to read this state file. If Mallory exfiltrated this state file, they would be able to set up an impersonating Forgejo Runner. As Forgejo fed jobs to this runner's polling routine, the confidentiality of secrets would be exposed to the impersonating Forgejo Runner.
runner.timeout
Creating long-running workflow jobs would allow Mallory to impact the availability of Forgejo Runner for other users. For example, the default 3 hour job timeout and runner.capacity of 1 would allow a simple bash loop to prevent other jobs from running for 3 hours. These jobs can be cancelled if observed.
Local Network Resources
Regardless of all the configuration options we've reviewed so far, as long as Mallory is able to execute arbitrary workflows that they author, you're exposing local network resources to interaction from Mallory's changes. For example, if your Forgejo Runner instance is colocated on the same network as end-user laptops and desktops, Mallory could enumerate local network resources through subnet scanning and attempt to attack those machines over the network.
If desired, preventing this type of attack will require isolating the subnet that the Forgejo Runner machine is hosted upon, and then limiting cross-subnet access at a firewall. If the Forgejo Runner is hosted on a virtual machine, tagged VLANs can be enforced at the VM level in order to perform this configuration without requiring physical network changes. The specifics of these configurations are out-of-scope for this document as they depend on the wide variety of network configurations and equipment in the wild.
Patching
All security configurations degrade over time as vulnerabilities are discovered and exploited in the software that are used to host services. If a security vulnerability is discovered in any component of the system that Forgejo Runner is hosted upon, the expected protection won't be present. Ensure that your operating system and other software on the host is kept up-to-date with upstream security releases.
Forgejo and Forgejo Runner have security releases from time-to-time as new issues are discovered. In order to be informed when these releases are scheduled, you can subscribe to notifications from the forgejo/security-announcements repository on Codeberg.
Utilizing Docker within Actions
Forgejo Runner uses a container environment to run actions, but additional configuration is required to allow actions to access a container environment as well.
For example, a simple Forgejo workflow like this:
name: Docker Build
on:
push:
branches: [main]
jobs:
build:
runs-on: docker
steps:
- uses: actions/checkout@v6
- run: docker build -t my-app .
Will typically fail with an error like this:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Why doesn't it just work?
The job container that Forgejo Runner executes doesn't have access to a docker socket.
When the Forgejo Runner receives a job to execute, it will create a container which will be used to execute the job, the job container. The configuration of this container is usually defined by the labels on Forgejo Runner, but can also be configured within the job by the jobs.<job_id>.container.image (and related properties).
The individual steps in the workflow are executed within that job container. Without any special configuration, when a command like docker build executes within the job container it will attempt to reach out to a running docker daemon via the default UNIX socket /var/run/docker.sock. Accessing this socket will fail because there is no docker daemon available within the job container, even though the job container itself is being run and managed by docker.
As there are a number of different systems being described in this document, these terms will be used consistently to maintain clarity:
- Host
- The machine that is running the Forgejo Runner process.
- Job container
- When executing a job (example: the
buildsection of the above workflow), Forgejo Runner typically uses a container environment (Docker, Podman, or LXC) to start a job container. Forgejo Runner executes commands from the job in this job container. (Forgejo Runner can also execute commands directly on the host via a label...:host; as that configuration is not affected by the problem described it is out-of-scope of this document.)
- When executing a job (example: the
- Service container
- Some jobs define additional containers that are started at the same time as the job container. For example, it is typical to start a database engine such as MySQL or PostgreSQL in order to support integration testing with a database. Service containers are defined in the
jobs.<job_id>.servicessection of the workflow file.
- Some jobs define additional containers that are started at the same time as the job container. For example, it is typical to start a database engine such as MySQL or PostgreSQL in order to support integration testing with a database. Service containers are defined in the
Security Considerations
Providing Forgejo Actions workflows the ability to access a Docker daemon adds to the complex security requirements for Forgejo Runner. While choosing their desired configuration, an administrator of Forgejo Runner will want to be cautious that providing access to a Docker system doesn't grant unexpected access to other resources in the same Docker system.
For example, assuming that both Forgejo and Forgejo Runner are using the same Docker daemon, granting direct access to that daemon would allow an executed Action to shut down the Forgejo instance (see diagram below), or perform far more dangerous actions like creating an administrator account with unlimited access to the instance. This may be an acceptable risk to the administrator, or it may be access that they would prefer to prevent.
Solutions
Docker-in-Docker
Forgejo Runner can be configured to use "Docker in Docker" (abbreviated as DIND) for Actions' access to Docker. This involves running a separate "DIND" container that runs on the host's Docker daemon, and provides its own isolated Docker daemon. When configured in this mode, attempts to access Docker will be redirected to the DIND container and will run there:
This configuration prevents access to the host's Docker resources, as they are not available within the job container:
Configuration
For this configuration, there are three steps:
- Run a Docker-in-Docker container
- Configure Forgejo Runner to access the Docker-in-Docker container when it starts job, service, and step containers.
- Provide a
DOCKER_HOSTenvironment variable to the job, service, and step containers that also allows access to the Docker-in-Docker container.
Below is a configuration example, but applying each step may vary depending on your Forgejo Runner installation and configuration.
For the first step, starting a Docker-in-Docker container, we'll use docker run to create a new container based upon the docker:dind image, and expose access to it on a local network port 2376:
docker run \
-p 127.0.0.1:2376:2375 \
--detach \
--privileged \
--restart always \
--name dind_container \
docker:dind \
dockerd -H tcp://0.0.0.0:2375 --tls=false
This configuration is creating a Docker-in-Docker instance on dind_container:2375 and 127.0.0.1:2376 with no encryption (--tls=false) and no access control. This instance will be accessible from the host, but not other machines on the local network; this may not be appropriate if there are other untrusted users on the host.
For step two and step three, we'll tweak the Forgejo Runner configuration file; see the inline comments for more details:
runner:
# ... skipping other configuration values you may have ...
envs:
# Step 3 (Part 1):
# A container will be setup for each job, and each step will
# be executed in it.
# This DOCKER_HOST environment variable will be used at each exec
# in that container, like docker exec -e DOCKER_HOST=tcp://dind_container.docker.internal:2375 <command>
# When you run the docker CLI or related
# commands, they'll find this variable and reach out to the DIND
# container to perform work there.
DOCKER_HOST: tcp://dind_container.docker.internal:2375
# ... skipping ...
container:
# ... skipping ...
# Step 2:
# Use the Docker-in-Docker host to run any job, service, and step
# containers. You can get a pretty functional step if you skip this
# step, as containers will still be able to access docker through
# the `runner.envs.DOCKER_HOST` setting -- but containers created
# by a job won't be able to access services. For example, having a
# `postgresql` service in the job, and then invoking `docker run -e
# DB_HOST=postgresql some-app:testing`, the two containers will both
# be in the Docker-in-Docker environment and will communicate freely.
docker_host: 'tcp://127.0.0.1:2376'
#
# Step 3 (Part 2):
# DNS resolve `dind_container` in the context of the container's
# host (the DIND container) to allow tcp://... to find it.
options: '--add-host=dind_container.docker.internal:host-gateway'
# ... skipping ...
If you're using the OCI image installation configuration described in the Forgejo Runner installation guide, then you can modify this configuration by setting runner.envs.DOCKER_HOST to the same docker-in-docker container is already present in that guide. A detailed example docker-compose.yml is available which writes an appropriate configuration file and updates the forgejo-runner daemon to use that configuration.
Administrator Notes
The Docker-in-Docker configuration is relatively easy to set up, and provides security by preventing access to other resources on the host's docker system.
However, there are some issues for administrators to be aware of:
- If a Forgejo Runner is configured with
runner.capacitygreater than 1 to allow multiple concurrent tasks, or multiple Forgejo Runners are configured using the same DINDcontainer.docker_host, then job containers will be able to view and interact with each other through the docker CLI. This could allow one to compromise the secrets of another running job container or interact with its files while it is running. - The DIND container will have its own storage, in its own
/var/lib/dockerdirectory within the container. This means that...- Images available on the host won't be available on the DIND container until pulled within that container,
- Extra storage may be used holding duplicate copies of images relative to the host,
- The
/var/lib/dockerdirectory will be wiped if the DIND container is recreated (for example, for software upgrades to thedocker:dindimage) unless it is volume-mapped to a persistent volume, potentially impacting the performance of future workflow runs if image caching is relied upon. - If
/var/lib/dockeris volume-mapped to a persistent volume, it will need periodic maintenance such asdocker system pruneto ensure that the images and volumes do not grow indefinitely with out-of-date and irrelevant contents.
- Artifacts that workflows create, such as new images created by
docker buildor containers created bydocker run, will be left in the DIND container. Any future workflow execution on the same runner will be able to inspect these artifacts and could extract confidential information from them. - Resource Constraints configured on Forgejo Runner won't have any impact on containers created from workflow actions. However, resources will be constrained to those configured on the created DIND container.
Configure container.docker_host to automount
Forgejo Runner can be configured to expose the host's Docker daemon to job containers by mounting the host's UNIX socket into /var/run/docker.sock within the job containers.
Configuration
container:
# ... skipping ...
#
# The "automount" option makes Forgejo Runner automatically find the
# appropriate docker socket in order to start job containers, and then
# also direct new job containers to have that docker socket mounted
# into them as `/var/run/docker.sock`.
docker_host: 'automount'
# ... skipping ...
The valid values for this configuration, and how they relate to accessing Docker from within actions, are:
docker_host: "-"- This is the default value.
- Forgejo Runner will attempt to use the environment variable
DOCKER_HOSTto connect to docker, and if that variable is not available it will use the UNIX socket/var/run/docker.sock. - Forgejo Runner will not share the socket with the containers.
- This prevents access to docker, but it is the default so that no unexpected security risks are present without the administrator knowingly choosing a different value.
docker_host: "automount"- Forgejo Runner will attempt to use the environment variable
DOCKER_HOSTto connect to docker, and fallback to the UNIX socket/var/run/docker.sock. - Forgejo Runner will share the socket with job/service/step containers by mounting it to
/var/run/docker.sock, if the socket is a UNIX socket. IfDOCKER_HOSTis atcp://...socket, then it cannot be shared in this manner and"automount"will act the same as"-".- In the OCI image installation installation method,
"automount"will not work becauseDOCKER_HOSTis atcp://...address. This option is incompatible with this approach to share the Docker socket.
- In the OCI image installation installation method,
- This allows containers access to docker; please review the Administrator Notes below for security details.
- Forgejo Runner will attempt to use the environment variable
docker_host: "unix://..."ordocker_host: "tcp://..."- Another URL can be used to provide a specific docker daemon.
- Forgejo Runner will share the socket with the containers as long as it's a UNIX socket or a named pipe.
- This allows containers access to docker; please review the Administrator Notes below for security details.
Administrator Notes
This configuration is the simplest in order to access Docker from a job container, but provides no security isolation. The docker daemon that Forgejo Runner discovers through its autodiscovery, which is typically the host's Docker daemon at /var/run/docker.sock, will be exposed to action workflows, and all services hosted on that Docker daemon can be inspected and mutated. This configuration is only advisable if measures are taken to ensure that only trusted users are able to access the Forgejo instance.
Risks to be aware of in choosing this approach include:
- Any containers executed in Docker will be accessible to the job, service, and step containers. For example, if Forgejo or Forgejo Runner are run as containers on the same host, then job containers will be able to execute actions like
docker kill,docker inspect, ordocker execon those containers. - All storage on the host can be compromised by an Action performing volume mounts to newly created containers. For example, an action step such as
docker run -v /:/host-mount ubuntuwould make the entire host's storage available to the container, including any on-disk secrets. - Artifacts that workflows create, such as new images created by
docker buildor containers created bydocker run, will be left in the host's docker daemon. Any future workflow execution on the same runner will be able to inspect these artifacts and could extract confidential information from them. - Resource Constraints configured on Forgejo Runner won't have any impact on containers created from workflow actions.
- Specific network configuration via Forgejo Runner's
container.networksetting will not be applied to any containers created from workflows. Workflows would be able to define settings like--network=hostto access the host operating system's native network. This creates new security risks by exposing Local Network Resources to these containers.
LXC
LXC is a lightweight virtualization method which allows for the creation of Linux containers which are capable of independently running Docker. When Forgejo Runner is configured with a label that runs job containers under LXC, those containers can provide a completely isolated environment for execution of Docker builds without putting host resources at risk.
Configuration
The Forgejo Runner installation guide contains a section Setting up the container environment which describes the requirements for configuring LXC access.
Once a Forgejo Runner is configured with a label that uses the LXC container environment (eg. bookworm-lxc:lxc://debian:bookworm), the job container will run under LXC. It will be straightforward to install and interact with Docker within an action:
name: Test Action
on:
pull_request:
jobs:
check-1:
runs-on: bookworm-lxc
steps:
- run: apt update && apt --quiet install --yes -qq docker.io
- run: docker ps -a
- run: docker run --rm -it ubuntu echo "Hello, world!"
Administrator Notes
Relative to the Docker-in-Docker and automount options, the LXC configuration provides superior isolation; accessing any Docker resource hosted within the job container is isolated from the runner host and from other job containers.
Using an LXC for job containers does have functional gaps. Service containers are not supported in an action when using the LXC hosting model.
As each LXC container will create its own independent Docker daemon with its own storage, there will be no image caching shared between runs. While this is a strong solution with respect to security risks, it can impact the performance of future workflow runs which will have no cached images during operations.
No solution can provide a guarantee of zero security risks. The LXC container model operates with a shared Linux kernel between multiple lightweight virtual environments. Keeping up-to-date with OS security patches is advised as container escape mechanisms have occasionally been discovered.
Forgejo Runner in a VM
Depending on the criticality and confidentiality of other services and data that are shared on the Forgejo Runner's host, you may wish to completely isolate Forgejo Runner in its own virtual machine. Once isolated, any of the other configurations described here may be applied to the VM in order to allow access to Docker services. As the underlying host OS will not be accessible to the virtual machine, many of the security risks described are no longer in-scope.
Forgejo Runner Configuration
Choosing labels
When Forgejo identifies a job that needs to be run, it inspects the jobs' runs-on field to evaluate which Forgejo Runner instances are capable of running the job. runs-on is typically a single value like ubuntu-latest, but in
more complex configurations it can also contain multiple values.
When configuring a Forgejo Runner, labels must be defined in the configuration file which indicate what jobs this runner should execute. For example:
# ... the rest of the config file ...
runner:
# ... other runner options ...
labels:
- debian:docker://docker.io/library/node:lts
- ubuntu-latest:docker://ghcr.io/catthehacker/ubuntu:act-22.04
# ... the rest of the config file ...
This would cause a job with runs-on: debian to run on this Forgejo Runner within the docker image docker://docker.io/library/node:lts.
A label has the following structure:
<label-name>:<label-type>://<default-image>
The label name is a unique string that identifies the label. It is the part that is specified in the runs-on field
of workflows to choose which runners the workflow can be executed on.
The label type determines what containerization system will be used to run the workflow. There are three options:
dockerfor Docker or Podman containerization,lxcfor LXC containerization,hostfor no containerization.
Docker or Podman
[!caution] Caution When starting a container, Forgejo Runner does the equivalent of
docker run <image>. That means that container images, once downloaded, are never updated.
If a label specifies docker as its label type, the rest of it is interpreted as the default container image to use.
The runner will execute all the steps, as root, within a container created from that image.
Label examples:
node20:docker://node:20-bookwormdefinesnode20to be thenode:20-bookwormimage from hub.docker.com.node20:docker://docker.io/node:20-bookwormwould have the same behaviour, fully defining the host to fetch the image from.docker:docker://data.forgejo.org/oci/alpine:3.20definesdockerto be thealpine:3.20image from https://data.forgejo.org/oci/-/packages/container/alpine/3.20.
It is generally recommended to pin image versions instead of relying on tags, for example, debian:docker://node@sha256:91447bc57243b852a21e0ff3553f531f0d4b66257a564b106c79d9e00f3aa14e instead of debian:docker://node:lts. That is a prerequisite for reproducible jobs.
To mimic GitHub runners, a label such as ubuntu-latest:docker://node:lts can be used. With this, the
Forgejo Runner will respond to runs-on: ubuntu-latest and will use the node:lts image from hub.docker.com.
This image is quite capable of running many of the workflows that are designed for the GitHub runners, but not all
the same tools will be available as GitHub provides.
Many common actions (for example, uses: actions/checkout@v6) require Node.js in the image.
The container image specified in the label can be overridden by a workflow by using
jobs.<job_id>.container.
LXC
If a label specifies lxc as its label type, the rest of it is interpreted as template[:release[:lxc-helper config]] where:
template[:release]is the template and release to use.lxc-helper configis the value of the --config option of lxc-helper used when creating a container.
The runner will execute all the steps, as root, within a LXC container created from that
template and release. The default template is debian and the default release is bullseye.
nodejs version 20 is installed.
Label examples:
bookworm:lxc://debian:bookworm:lxc dockerdefinesbookwormto be an LXC container running Debian GNU/Linux bookworm. It has the necessary capabilities to run a nested LXC container and a Docker engine.bookworm:lxc://debian:bookwormdefinesbookwormto be an LXC container running Debian GNU/Linux bookworm. It has the necessary capabilities to run a nested LXC container, KVM virtual machines and a Docker engine.
Host
If a label specifies host as its label type, the runner will execute all the steps in a shell forked from the
runner, directly on the host.
[!warning] Warning There is no isolation at all and a single job can permanently destroy the host.
Label example:
self-hosted:hostdefines jobs that haveruns-on: self-hostedto run without any container isolation.
Special labels
Runner labels can also be used to define other special features a runner has.
For example, if three runners were available for executing jobs and all of them defined the
docker::docker://node:20-bookworm label, then jobs would be distributed between each of the three runners as they are
available. If only a single one of these runners had a special hardware capability like a GPU, there may be jobs that
require that capability and can't be run on the other systems.
The solution in this case is to add multiple labels to a job's runs-on field:
on: pull_request
jobs:
my-job:
runs-on: [docker, gpu]
# ...
And then to specify both the docker:... and gpu:... labels in the Forgejo Runner configuration:
runner:
labels:
- docker:docker://ghcr.io/catthehacker/ubuntu:act-22.04
- gpu:docker://ghcr.io/catthehacker/ubuntu:act-22.04
Only a Forgejo Runner with both the docker and gpu labels will be able to run the job. The job will be run on
the containerization platform that is listed in the first label in the runs-on array.
Enabling IPv6 in Docker & Podman networks
When a Forgejo Runner creates its own Docker or Podman networks, IPv6 is not enabled by default, and must be enabled explicitly in the Forgejo Runner configuration.
Docker only: The Docker daemon requires additional configuration to enable IPv6. To make use of IPv6 with Docker,
you need to provide a /etc/docker/daemon.json configuration file with at least the following keys:
{
"ipv6": true,
"experimental": true,
"ip6tables": true,
"fixed-cidr-v6": "fd00:d0ca:1::/64",
"default-address-pools": [
{ "base": "172.17.0.0/16", "size": 24 },
{ "base": "fd00:d0ca:2::/104", "size": 112 }
]
}
Afterwards restart the Docker daemon with systemctl restart docker.service.
[!note] Note These are example values. While this setup should work out of the box, it may not meet your requirements. Please refer to the Docker documentation regarding enabling IPv6 and allocating IPv6 addresses to subnets dynamically.
Docker & Podman: To test IPv6 connectivity in Forgejo-Runner-created networks, create a small workflow such as the following:
---
on: push
jobs:
ipv6:
runs-on: docker
steps:
- run: |
apt update; apt install --yes curl
curl -s -o /dev/null http://ipv6.google.com
If you run this action with forgejo-runner exec, you should expect this job fail:
$ forgejo-runner exec
...
| curl: (7) Couldn't connect to server
[ipv6.yml/ipv6] ❌ Failure - apt update; apt install --yes curl
curl -s -o /dev/null http://ipv6.google.com
[ipv6.yml/ipv6] exitcode '7': failure
[ipv6.yml/ipv6] Cleaning up services for job ipv6
[ipv6.yml/ipv6] Cleaning up container for job ipv6
[ipv6.yml/ipv6] Cleaning up network for job ipv6, and network name is: FORGEJO-ACTIONS-TASK-push_WORKFLOW-ipv6-yml_JOB-ipv6-network
[ipv6.yml/ipv6] 🏁 Job failed
To actually enable IPv6 with forgejo-runner exec, the flag --enable-ipv6 must be provided. If you run this again
with forgejo-runner exec --enable-ipv6, the job should succeed:
$ forgejo-runner exec --enable-ipv6
...
[ipv6.yml/ipv6] ✅ Success - Main apt update; apt install --yes curl
curl -s -o /dev/null http://ipv6.google.com
[ipv6.yml/ipv6] Cleaning up services for job ipv6
[ipv6.yml/ipv6] Cleaning up container for job ipv6
[ipv6.yml/ipv6] Cleaning up network for job ipv6, and network name is: FORGEJO-ACTIONS-TASK-push_WORKFLOW-ipv6-yml_JOB-ipv6-network
[ipv6.yml/ipv6] 🏁 Job succeeded
Finally, if this test was successful, enable IPv6 in the runner-config.yml file of the Forgejo Runner daemon and
restart the daemon:
container:
enable_ipv6: true
Now, Forgejo Runner will create networks with IPv6 enabled, and workflow containers will be assigned addresses from the pools defined in the Docker daemon configuration.
IPv6 connectivity issues with rootless podman
Because creation of real networks is limited to the root user, rootless podman cannot create actual networks. To work
around this issue, podman creates so-called tap-networks which come with their own
limitations. At the time of writing, only Podman version
5.3 and later have been observed to have proper IPv6 support in rootless bridge networks. Podman 5 switched to
passt for rootless networking and 5.3 includes fixes like host-service reachability.
(Earlier versions appear to cause "host unreachable" or "network unreachable" issues with bridge networks - which is the
type Forgejo Runners create.)
Cache configuration
Some actions such as https://data.forgejo.org/actions/cache or https://data.forgejo.org/actions/setup-go can
communicate with the Forgejo Runner to save and restore commonly used files such as compilation dependencies. They are
stored as compressed tar archives, fetched when a job starts and saved when it completes.
If the machine has a fast disk, uploading the cache when the job starts may significantly reduce the bandwidth required to download and rebuild dependencies.
For more information see the cache section of the runner configuration file.
Configuration file reference
Installation procedures for Forgejo Runner describe how to generate a default configuration file, eg. for a binary installation or for a docker compose installation. Those procedures are recommended to ensure the correct configuration file format is provided, with all the available options that match the version of Forgejo Runner that you are installing.
All Forgejo Runner configuration options are documented in the default configuration file in YAML comments.
The most recent version of the configuration file can be viewed in the forgejo/runner repo.
Installation from binary
System requirements
Forgejo Runner requires that Git is installed, and has been tested with a minimum version of Git 2.24.3.
Downloading and installing the binary
Download the latest binary release and verify its signature:
$ export ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
$ export RUNNER_VERSION=$(curl -X 'GET' https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest | jq .name -r | cut -c 2-)
$ export FORGEJO_URL="https://code.forgejo.org/forgejo/runner/releases/download/v${RUNNER_VERSION}/forgejo-runner-${RUNNER_VERSION}-linux-${ARCH}"
$ wget -O forgejo-runner ${FORGEJO_URL} || curl -o forgejo-runner ${FORGEJO_URL}
$ chmod +x forgejo-runner
$ wget -O forgejo-runner.asc ${FORGEJO_URL}.asc || curl -o forgejo-runner.asc ${FORGEJO_URL}.asc
$ gpg --keyserver hkps://keys.openpgp.org --recv EB114F5E6C0DC2BCDD183550A4B61A2DC5923710
$ gpg --verify forgejo-runner.asc forgejo-runner && echo "✓ Verified" || echo "✗ Failed"
Good signature from "Forgejo <contact@forgejo.org>"
aka "Forgejo Releases <release@forgejo.org>"
✓ Verified
Next, copy the downloaded binary to /usr/local/bin and make it executable:
$ cp forgejo-runner /usr/local/bin/forgejo-runner
You should now be able to test the runner by running forgejo-runner -v:
$ forgejo-runner -v
forgejo-runner version v13.0.0
Setting up the runner user
Set up the user to run the daemon:
$ useradd --create-home runner
If the runner will be using Docker, ensure the runner user has access to
the docker socket. Run the following command to grant access to the docker socket:
$ usermod -aG docker runner
If the runner will be using Podman, no particular permissions are needed, but you will need to configure your system to run the Podman service. See the Podman section below for more information.
Setting up the container environment
The Forgejo Runner relies on application containers (Docker, Podman, etc.) or system containers (LXC) to execute a workflow in an isolated environment. They need to be installed and configured independently.
It is common for workflows to also require interaction with a container environment, for example to execute docker build commands. This is distinct from how Forgejo Runner itself executes jobs, and is an optional configuration that is
described in detail in Utilizing Docker within Actions.
Docker
See the Docker installation documentation for more information.
Podman
On most recent Linux distributions, the podman package alone should
give you a working version of Podman which can run as the unprivileged
runner user. You do not need to install any supplemental packages
for Docker compatibility or "rootless" operation. However, some
configuration is necessary to ensure that the runner is started at
boot time and can access the Podman service.
These instructions are for Debian 13 (trixie) but should also apply to
Ubuntu, and likely to other systemd based distributions. They
assume you have downloaded and installed the forgejo-runner and
created the runner user as described above.
First, install podman:
sudo apt install podman
Next, you will configure systemd to create a socket which will launch
Podman as the runner user when accessed by forgejo-runner. As your
normal user (it is not necessary to enable logins for runner),
run:
sudo systemctl --user -M runner@ enable --now podman.socket
Verify that the socket is enabled and find its location:
$ sudo systemctl --user -M runner@ status podman.socket
● podman.socket - Podman API Socket
Loaded: loaded (/usr/lib/systemd/user/podman.socket; enabled; preset: enabled)
Active: active (listening) since Wed 2026-04-29 16:47:03 EDT; 78ms ago
Invocation: 18779cd4131144e197422ade1c100a0e
Triggers: ● podman.service
Docs: man:podman-system-service(1)
Listen: /run/user/1004/podman/podman.sock (Stream)
Use the path to this socket shown above in the docker_host parameter
when configuring the runner.
Finally, you must set the runner user to "linger" so that the socket
will be created at boot:
sudo loginctl enable-linger runner
On non-systemd distributions, you are free to choose any path you like
for the podman socket (for instance, /home/runner/podman.sock). It
can then be provided by running, as the runner user:
podman system service -t 0 unix:///home/runner/podman.sock
See the documentation for your distribution's init system for how to ensure this is started at boot time.
LXC
For jobs to run in LXC containers, the Forgejo Runner needs passwordless sudo access for all lxc-* commands on a
Debian GNU/Linux bookworm system where LXC is installed. The LXC
helpers can be used as follows to create a suitable container:
$ git clone https://code.forgejo.org/forgejo/lxc-helpers
$ sudo cp -a lxc-helpers/lxc-helpers{,-lib}.sh /usr/local/bin
$ lxc-helpers.sh lxc_container_create myrunner
$ lxc-helpers.sh lxc_container_start myrunner
$ lxc-helpers.sh lxc_container_user_install myrunner 1000 debian
[!note] Note Multiarch Go builds and binfmt need
bookwormto produce and test binaries on a single machine for people who do not have access to dedicated hardware.
The Forgejo Runner can then be installed and run within the myrunner container.
$ lxc-helpers.sh lxc_container_run forgejo-runners -- sudo --user debian bash
$ sudo apt-get install docker.io wget gnupg2
$ wget -O forgejo-runner https://code.forgejo.org/forgejo/runner/releases/download/v13.0.0/forgejo-runner-13.0.0-linux-amd64
...
[!warning] Warning LXC containers do not provide a level of security that makes them safe for potentially malicious users to run jobs. They provide an excellent isolation for jobs that may accidentally damage the system they run on.
Host
There are no requirements for jobs that run directly on the host.
[!warning] Warning There is no isolation at all and a single job can permanently destroy the host.
[!warning] Warning Processes forked out of a job may linger after the job is complete, possibly forever, if the job fails to wait for them to complete.
Configuration
As the runner user, generate the default configuration file for
Forgejo Runner, and store it in a file accessible to the user
(/home/runner/runner-config.yml for instance):
forgejo-runner generate-config > /home/runner/runner-config.yml
Forgejo Runner needs to be configured and registered with Forgejo before it can be started successfully. Configure
Forgejo Runner, editing /home/runner/runner-config.yml file as you
proceed.
[!note] Note Forgejo Runner requires the configuration file to be explicitly specified with the
-ccommand-line option. There is no default configuration file location.
Podman socket configuration
When using Podman, you must configure the docker_host parameter in
the container section of the configuration file with the path to the
Podman socket created above. For example, if the output of
systemctl shows the socket as /run/user/1004/podman/podman.sock,
then the configuration would be:
container:
docker_host: unix:///run/user/1004/podman/podman.sock
You can also set this with the DOCKER_HOST environment variable.
As a reminder, to find the path to the podman socket, as the runner
user, you can run:
XDG_RUNTIME_DIR=/run/user/$(id -u) systemctl --user status podman.socket
Note that the XDG_RUNTIME_DIR environment variable required by
systemctl is not set in non-login shells.
Starting the runner
After the runner has been registered, it can be started by running forgejo-runner daemon as the runner user, in the
home directory:
$ whoami
runner
$ pwd
/home/runner
$ forgejo-runner daemon -c runner-config.yml
INFO[2024-09-14T19:19:14+02:00] Starting runner daemon
Running as a systemd service
To automatically start the runner when the system starts, copy
forgejo-runner.service to
/etc/systemd/system/forgejo-runner.service.
Then run systemctl daemon-reload to reload the unit files. Run systemctl start forgejo-runner.service to test the
new service. If everything works, run systemctl enable forgejo-runner.service to enable auto-starting the service on
boot.
Use journalctl -u forgejo-runner.service to read the runner logs.
Installation with Docker
OCI image
The OCI images are built from the Dockerfile which is
found in the source directory. It contains the
forgejo-runner binary.
$ docker run --rm data.forgejo.org/forgejo/runner:13 forgejo-runner --version
forgejo-runner version v13.0.0
It does not run as root:
$ docker run --rm data.forgejo.org/forgejo/runner:13 id
uid=1000 gid=1000 groups=1000
Docker Compose
One way to run the Docker image is via Docker Compose. To do so, as root, 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
After running this script with bash setup.sh, define the following docker-compose.yml:
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:13'
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: 'forgejo-runner daemon --config runner-config.yml'
Generate the default configuration file for Forgejo Runner, and store it in the data directory created previously:
$ docker run --rm data.forgejo.org/forgejo/runner:13 \
forgejo-runner generate-config > data/runner-config.yml
Forgejo Runner needs to be configured and registered with Forgejo before it can be started successfully. Configure
Forgejo Runner, editing data/runner-config.yml file as you proceed.
Once the configuration is complete, you can start the runner by executing docker compose up -d.
More docker compose examples are provided to demonstrate how to install the OCI image to successfully run a workflow.
Installation from packaging
NixOS
A forgejo-runner package is available for Nix.
As NixOS service module services.gitea-actions-runner.* can be used.
If application containers are to be used (Docker or Podman), one of virtualisation.docker.enable or virtualisation.podman.enable must also be set to true.
An example service definition might look like this:
services.gitea-actions-runner = {
package = pkgs.forgejo-runner;
instances.my-forgejo-instance = {
enable = true;
name = "my-forgejo-runner-01";
token = "<registration-token>";
url = "https://code.forgejo.org/";
labels = [
"node-22:docker://node:22-bookworm"
"nixos-latest:docker://nixos/nix"
];
settings = { ... };
};
The runner configuration can be specified in services.gitea-actions-runner.instances.<instance>.settings as per Configuration.
IPv6 support is not enabled by default for docker. The following snippet enables this.
virtualisation.docker = {
daemon.settings = {
fixed-cidr-v6 = "fd00::/80";
ipv6 = true;
};
};
If you would like to use docker runners in combination with cache actions, be sure to add docker bridge interfaces "br-*" to the firewalls' trusted interfaces:
networking.firewall.trustedInterfaces = [ "br-+" ];
Forgejo Runner Registration
Forgejo Runner requires a uuid and token to connect to Forgejo. These values act as a username and password for identifying and authenticating the connection. There are three ways to obtain them:
- Interactive registration in Forgejo (recommended)
- Using Forgejo's HTTP API
- Offline registration using Forgejo's command line utility
Interactive registration
Forgejo Runner needs to be registered with Forgejo to execute jobs. Within Forgejo, a user can register a new runner in four different locations, which vary in which workflows will be executed on the runner:
- A system administrator can create a runner at
/admin/actions/runners, which will execute workflows from all repositories on the Forgejo instance. - An organization administrator can create a runner at
/org/{org}/settings/actions/runnerswhich will execute workflows from all the repositories within the organization. - A user can create a runner at
/user/settings/actions/runnerswhich will execute workflows from all repositories owned by the user. - A repository administrator can create a runner at
/{owner}/{repository}/settings/actions/runnerswhich will execute workflows from the single repository.
Each of these pages will display any existing runners that are capable of executing jobs, including runners from higher level registrations. For example, when viewing the runner list in a repository, that will include any runners registered with the organization that owns the repository, and any runners registered at the system administrator level.
The Create new runner button opens a dialog to enter a name and description for the runner:
After hitting the Create button, a unique identifier (UUID) and a confidential secret (Token) will be displayed for
the new runner.
The displayed configuration should be copied into the Forgejo Runner configuration file, in the server section. The
default configuration file (which can be generated if absent) has an empty
server section which contains only comments about the intended format. For example, to finish this configuration based
upon the UI values:
# ... the rest of the config file ...
host:
# The parent directory of a job's working directory.
# If it's empty, $HOME/.cache/act/ will be used.
workdir_parent:
server:
connections:
forgejo:
url: https://example.com/
uuid: 33834eef-e758-48c4-a676-1745426747aa
token: d4fe2db46a4c6bdc434a9ce3378d9a1489c1b30e
The registration process can be repeated multiple times to connect one Forgejo Runner to different Forgejo instances, or to different organizations, users, or repositories within a single Forgejo instance. In this case, multiple connections would end up in the configuration file, for example:
# ... the rest of the config file ...
server:
connections:
company-org1: # keys must be unique, but have no meaning
url: https://company-forgejo.example.com/
uuid: 33834eef-e758-48c4-a676-1745426747aa
token: d4fe2db46a4c6bdc434a9ce3378d9a1489c1b30e
my-org2:
url: https://my-forgejo.example.org/
uuid: f7fb6f55-7140-44b6-893a-37ceae0df435
token: 111b5aca20e64c290d639987c9a401377a2e4253
Offline registration
When Infrastructure as Code (Ansible, kubernetes, etc.) is used to deploy and configure both Forgejo and the Forgejo runner, it may be more convenient for it to generate a secret and share it with both. This method of registration is not applicable for hosted services like Codeberg as it requires system administrator access to the Forgejo instance.
The forgejo forgejo-cli actions register --secret <secret> subcommand can be used to register the runner with the
Forgejo instance. First, generate a 40-character long string of hexadecimal numbers.
On the machine running Forgejo:
$ forgejo forgejo-cli actions register --name runner-name --scope myorganization \
--secret 7c31591e8b67225a116d4a4519ea8e507e08f71f
37633331-3539-3165-3862-363732323561
And on the Forgejo Runner instance, add the remote server address, uuid, and token to the configuration file:
# ... the rest of the config file ...
server:
connections:
forgejo:
url: https://example.com/
uuid: 37633331-3539-3165-3862-363732323561
token: 7c31591e8b67225a116d4a4519ea8e507e08f71f
The 40-character secret is a combination of an identifier and a secret value: the first 16 characters will be used as an identifier for the runner, while the rest is the actual secret. It is possible to update the secret of an existing runner by running the command again on the Forgejo machine, with the last 24 characters updated.
For instance, the command below would change the secret set by the previous command:
$ forgejo forgejo-cli actions register --name runner-name --scope myorganization \
--secret 7c31591e8b67225a84e8e06633b9578e793664c3
# ^^^^^^^^^^^^^^^^ This part is identical
Runner Modes
Forgejo distinguishes two types of runner modes: ephemeral and persistent (the default). Persistent runners can run multiple jobs during their lifetime, whereas ephemeral runners can run at most one job before they get deleted by Forgejo. Ephemeral mode has security benefits in environments that create runner instances on demand.
Ephemeral mode can be enabled during runner registration by passing --ephemeral to forgejo-runner register (deprecated) or forgejo forgejo-cli actions register. The HTTP API exposes an ephemeral property, too. Ephemeral mode is enforced by Forgejo and cannot be disabled or ignored.