![]() |
VOOZH | about |
Documentation for my Forgejo server and runner setup.
Apache virtual host which reverse proxies to an ephemeral,
rootless Podman container running as a dedicated forgejo user
and managed by a systemd service.
Apache virtual host which reverse proxies to the Forgejo
container bound to port 8888.
Contents of /etc/apache2/sites-available/git.pmdn.org.conf:
<VirtualHost _default_:80>
ServerName git.pmdn.org
# unconditionally rewrite to https://git.pmdn.org
RewriteEngine On
RewriteRule ^/(.*)$ https://git.pmdn.org/$1 [R,L]
# logging
LogLevel warn
CustomLog /data/www/git.pmdn.org/logs/access.log combined
ErrorLog /data/www/git.pmdn.org/logs/error.log
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName git.pmdn.org
# tls
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/git.pmdn.org/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/git.pmdn.org/privkey.pem
# reverse proxy configuration
# ref: https://forgejo.org/docs/latest/admin/setup/reverse-proxy/#apache
ProxyRequests off
ProxyPreserveHost on
AllowEncodedSlashes NoDecode
# reverse proxy to port 8888
ProxyPass / http://localhost:8888/ timeout=5 upgrade=websocket
ProxyPassReverse / http://localhost:8888
# logging
LogLevel warn
CustomLog /data/www/git.pmdn.org/logs/access.log combined
ErrorLog /data/www/git.pmdn.org/logs/error.log
</VirtualHost>
</IfModule>
Rewrite*
directives.Proxy* directives.ProxyPass options to resolve timeout issuesA systemd user service which manages the Forgejo container.
The Forgejo container is ephemeral, rootless, bind mounts the named forgejo-data volume, and binds to host port 8888.
Note: Normally you would also forward a second host port (e.g.
2222) to container port 22 to expose SSH. This
configuration doesn’t do that because external SSH is blocked unless
users are connected via the VPN.
Contents of ~forgejo/.config/systemd/user/forgejo.service:
[Unit]
Description=Forgejo Server
[Service]
# start ephemeral container named "forgejo" with the following
# parameters:
#
# - image: codeberg.org/forgejo/forgejo:15-rootless
# - pass UID, GID, and root URL as environment variables
# - bind mount named volume "forgejo-data"
# (a named volume is required for rootless forgejo)
# - forward host port 8888 to container port 3000
ExecStart=/usr/bin/podman run --rm --name forgejo \
-e USER_UID=%U \
-e USER_GID=%G \
-e ROOT_URL=https://git.pmdn.org \
-p 8888:3000 \
-u %U:%G \
-v forgejo-data:/var/lib/gitea \
-v /etc/localtime:/etc/localtime:ro \
codeberg.org/forgejo/forgejo:15-rootless
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure
TimeoutSec=0
RestartSec=10
[Install]
# start at boot
WantedBy=default.target
data.forgejo.org mirror instead.Forgejo runners.
As of this writing I have the Forgejo runner installed on the following systems:
hive: x86-64 VM running Debianlemon: Raspberry Pi 5 running Raspberry Pi OSpizza: Odroid N2L running Armbianpumpkin: Raspberry Pi 5 running Raspberry Pi OSThe following sections explain how to create a dedicated runner user, install the runner binary, register the runner, configure the runner, and enable the runner systemd service.
Steps to create and configure the runner user:
# create a dedicated `runner` user
adduser runner
# enable linger for the `runner user
sudo loginctl enable-linger runner
# enable podman socket activation for runner user
sudo systemctl --user -M runner@ enable --now podman.socket
Follow these instructions to download, verify,
and install the forgejo-runner binary.
If those binary installation instructions are too fiddly, you can use my
install-runner.py script instead.
Example:
$ ./install-runner.py
INFO:__main__:checking for signing key EB114F5E6C0DC2BCDD183550A4B61A2DC5923710
pub ed25519 2022-11-16 [SC]
EB114F5E6C0DC2BCDD183550A4B61A2DC5923710
uid [ unknown] Forgejo <contact@forgejo.org>
uid [ unknown] Forgejo Releases <release@forgejo.org>
sub ed25519 2025-01-20 [S] [expires: 2027-01-10]
sub ed25519 2025-12-16 [S] [expires: 2027-06-09]
sub cv25519 2022-11-16 [E]
INFO:__main__:fetch https://code.forgejo.org/forgejo/runner/releases/download/v12.9.0/forgejo-r
unner-12.9.0-linux-arm64 (19595448 bytes)
INFO:__main__:fetch https://code.forgejo.org/forgejo/runner/releases/download/v12.9.0/forgejo-r
unner-12.9.0-linux-arm64.asc (228 bytes)
INFO:__main__:run /usr/bin/gpg --verify /tmp/tmpehuzc618/forgejo-runner-12.9.0-linux-arm64.asc
/tmp/tmpehuzc618/forgejo-runner-12.9.0-linux-arm64
gpg: Signature made Mon 20 Apr 2026 04:43:26 PM EDT
gpg: using EDDSA key 3BF4E813F84812411DA01E5BC4186DF66F4B6750
gpg: Good signature from "Forgejo <contact@forgejo.org>" [unknown]
gpg: aka "Forgejo Releases <release@forgejo.org>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: EB11 4F5E 6C0D C2BC DD18 3550 A4B6 1A2D C592 3710
Subkey fingerprint: 3BF4 E813 F848 1241 1DA0 1E5B C418 6DF6 6F4B 6750
INFO:__main__:run /usr/bin/sudo /usr/bin/install -v /tmp/tmpehuzc618/forgejo-runner-12.9.0-linu
x-arm64 /usr/local/bin/forgejo-runner
removed '/usr/local/bin/forgejo-runner'
'/tmp/tmpehuzc618/forgejo-runner-12.9.0-linux-arm64' -> '/usr/local/bin/forgejo-runner'
$ sudo systemctl --user -M runner@ restart forgejo-runner
Download “install-runner-example.txt”
Steps:
Example:
Register a new runner.
Create ~runner/.config/forgejo-runner/, then use generate-config to
populate config.yaml:
sudo -u runner sh -c '
mkdir -p ~runner/.config/forgejo-runner;
forgejo-runner generate-config > ~runner/.config/forgejo-runner/config.yaml
'
Edit ~runner/.config/forgejo-runner/config.yaml and add the generated
YAML block that you copied from the text field in the Register
Runner section.
At this point you’ll also want to add some runner labels. See the “Choosing Labels” section of the Forgejo Runner Administrator Guide.
As of this writing choosing labels is a bit fiddly. The labels that I am using are:
docker:docker://data.forgejo.org/oci/node:ltsubuntu-latest:docker://ghcr.io/catthehacker/ubuntu:rust-latestrust:docker://ghcr.io/catthehacker/ubuntu:rust-latestThe last two labels are act container images recommend in this post.
The act container images include development tools for languages
like Go and Rust. The images also include Node, so they
work as expected for common actions like actions/checkout,
actions/cache and actions/upload-artifact.
Here’s an example of a fully populated server section of
~runner/.config/forgejo-runner/config.yaml:
# example "server" section with populated labels# (note: token is secret and should not be shared)server:connections:pmdn:uuid:"2738dfaa-c05f-4b91-8ee8-4868316f969a"url:"https://git.pmdn.org/"token:"e531f31be9916a906799805cc857a238c4275621"labels:- "docker:docker://data.forgejo.org/oci/node:lts"- "ubuntu-latest:docker://ghcr.io/catthehacker/ubuntu:rust-latest"- "rust:docker://ghcr.io/catthehacker/ubuntu:rust-latest"- "rust-x86-64:docker://ghcr.io/catthehacker/ubuntu:rust-latest"To allow external networking from jobs, you may need to do the following:
~runner/.config/forgejo-runner/config.yaml.container section.network: "" to network: "host".To register the runner with another Forgejo instances (for example, Codeberg), follow the steps at the end of the “Interactive Registration” section.
Create the ~runner/.config/systemd/user directory:
# create ~runner/.config/systemd/user/
sudo -u runner mkdir -p ~runner/.config/systemd/user
Populate ~runner/.config/systemd/user/forgejo-runner.service:
[Unit]
Description=Forgejo Runner
Documentation=https://forgejo.org/docs/latest/admin/actions/
[Service]
# %E expands to $XDG_CONFIG_HOME (ex: "~/.config")
ExecStart=/usr/local/bin/forgejo-runner daemon --config %E/forgejo-runner/config.yaml
ExecReload=/bin/kill -s HUP $MAINPID
WorkingDirectory=%h
Restart=on-failure
TimeoutSec=0
RestartSec=10
# %t expands to $XDG_RUNTIME_DIR (ex: "/run/user/1000")
Environment=DOCKER_HOST=unix://%t/podman/podman.sock
[Install]
WantedBy=default.target
Download “forgejo-runner.service”
Enable and start the forgejo-runner user service:
# enable forgejo-runner user service
sudo systemctl --user -M runner@ enable forgejo-runner
# start forgejo-runner user service
sudo systemctl --user -M runner@ start forgejo-runner
Verify that the forgejo-runner user service is working as expected:
# check status of forgejo-runner user service
$ sudo systemctl --user -M runner@ is-active forgejo-runner
active
Edit history of this page.
forgejo.service.15-rootless image.server
section from ~runner/.config/forgejo-runner/configy.yaml.