[23may2026] setting up forgjo runner on a cloud host
Table of Contents
1. Problem
Now that git hosting is up, would like to also run github actions.
One advantage is we can setup a host runner so that we share the nix store between invocations
2. Plan
2.1. Forgejo Runner
2.1.1. Runtime environment for forgejo runner
We want to use nix for this:
# /etc/forgejo-runner/nix/flake.nix
{
description = "Forgejo runner environment";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system :
let pkgs = nixpkgs.legacyPackages.${system};
in {
packages.default = pkgs.forgejo-runner;
apps.forgejo-runner = {
type = "app";
program = "${pkgs.forgejo-runner}/bin/forgejo-runner";
};
devShells.default = pkgs.mkShell {
buildInputs = [ pkgs.forgejo-runner ];
};
}
);
}
2.1.2. Add runner to forgejo instance
Create runner on https://conybeare.us/git/admin/actions/runners. There's a "create new runner" button.
We call the new runner vpn1-host.
2.1.3. Configuration
Add config =/etc/forgejo-runner/config.yaml
server:
connections:
host-vpn1:
url: https://conybeare.us/git/
uuid: 16f1f804-2f00-43f2-8c40-9ce122fd3000
token: 9999999999999999999999999999999999999999
runner:
labels:
- host
executor: host
Verify by starting runner manually
$ sudo (cd /var/lib/forgejo-runner/nix && nix run . -- daemon --config /etc/forgejo-runner/config.yaml)
INFO[2026-05-23T18:26:30Z] Starting runner daemon
INFO[2026-05-23T18:26:30Z] runner: vpn1-host, with version: v12.10.1, with labels: [host], ephemeral: false, declared successfully
INFO[2026-05-23T18:26:30Z] [poller] launched
2.1.4. Add user
$ sudo useradd --system --shell /bin/bash --home /var/lib/forgejo-runner forgejo-runner $ mkdir -p /var/lib/forgejo-runner $ chown -R forgejo-runner:forgejo-runner /var/lib/forgejo-runner
2.1.5. Systemd setup (forgejo-runner)
Create /etc/systemd/system/forgejo-runner.service:
[Unit] Description=Forgejo Runner After=network.target forgejo.service [Service] Type=simple User=forgejo WorkingDirectory=/etc/forgejo-runner ExecStart=/nix/var/nix/profiles/default/bin/nix run \ /etc/forgejo-runner/nix#forgejo-runner -- daemon --config /etc/forgejo-runner/config.yaml Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target
################################################################
Add user
$ sudo useradd --system --shell /bin/false --home /var/lib/forgejo forgejo $ sudo mkdir -p /var/lib/forgejo /var/log/forgejo $ sudo mkdir -p /var/lib/forgejo/lfs $ sudo mkdir -p /var/lib/forgejo/gitea-repositories $ sudo chown -R forgejo:forgejo /var/lib/forgejo /var/log/foregejo
Forgejo setup
$ sudo mkdir -p /etc/forgejo
create /etc/foregejo/app.ini:
[server] ROOT_URL = https://conybeare.us/git APP_DATA_PATH = /var/lib/forgejo HTTP_ADDR = 127.0.0.1 HTTP_PORT = 3000 START_SSH_SERVER = true SSH_LISTEN_PORT = 2222 SSH_USER = forgejo SSH_PORT = 2222 [database] DB_TYPE = sqlite3 PATH = /var/lib/forgejo/forgejo.db [repository] ROOT = /var/lib/forgejo/gitea-repositories [ui] DEFAULT_THEME = forgejo-dark INSTANCE_SLOGAN = Mad Science, all the way down [lfs] PATH = /var/lib/forgejo/lfs STORAGE_TYPE = local [log] ROOT_PATH = /var/log/forgejo MODE = file LEVEL = info [mailer] ENABLED = false
$ sudo chown forgejo:forgejo /etc/forgejo/app.ini $ sudo chmod 600 /etc/forgejo/app.ini
2.1.6. Runtime environment for forgejo
We want to use nix for this:
# /etc/forgejo/nix/flake.nix
{
description = "Forgejo environment";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system :
let pkgs = nixpkgs.legacyPackages.${system};
in {
packages.default = pkgs.forgejo;
apps.forgejo = {
type = "app";
program = "${pkgs.forgejo}/bin/forgejo";
};
devShells.default = pkgs.mkShell {
buildInputs = [ pkgs.forgejo ];
};
}
);
}
To verify syntax (will also fetch all the deps)
$ nix flake show /etc/forgejo/nix $ ls /etc/forgejo/nix/flake.lock # now pinned
Also turns out this environment applies to systemd starting foregejo. This doesn't get inherited by CI jobs.
To make the second part easier, create a profile in the runner account
$ sudo -u forgejo-runner bash $ nix profile install nixpkgs#nodejs ... $ ls /var/lib/forgego-runner/.nix-profile/bin corepack node npm npx
2.1.7. Systemd setup (Forgejo)
[Unit] Description=Forgejo After=network.target [Service] Type=simple User=forgejo Group=forgejo WorkingDirectory=/var/lib/forgejo ExecStart=/nix/var/nix/profiles/default/bin/nix run /etc/forgejo/nix#forgejo -- -c /etc/forgejo/app.ini web Restart=always RestartSec=5 Environment="HOME=/var/lib/forgejo" [Install] WantedBy=multi-user.target
then setup
$ sudo systemctl daemon-reload $ sudo systemctl enable forgejo $ sudo systemctl start forgejo
2.1.8. Verify status
$ sudo systemctl status forgejo $ sudo journalctl -u forgejo -n 3 May 18 02:16:21 vpn1 nix[1043231]: Listen: http://127.0.0.1:3000 May 18 02:16:21 vpn1 nix[1043231]: AppURL(ROOT_URL): https://conybeare.us/ May 18 02:16:21 vpn1 nix[1043231]: Starting new Web server: tcp:127.0.0.1:3000 on PID: 1043231
2.2. Proxy Magic
2.2.1. Certificates
$ sudo /nix/var/nix/profiles/default/bin/nix run nixpkgs#certbot -- certonly --standalone -d conybeare.us
$ tree /etc/letsencrypt
/etc/letsencrypt/
├── accounts [error opening dir]
├── archive [error opening dir]
├── live [error opening dir]
├── renewal
│ └── conybeare.us.conf
└── renewal-hooks
├── deploy
├── post
└── pre
2.2.2. Nginx user
Add user
$ sudo useradd --system --shell /bin/false --home /var/cache/nginx nginx
Also working directories
$ sudo mkdir -p /var/cache/nginx $ sudo chown nginx:nginx /var/cache/nginx $ sudo chmod 755 /var/cache/nginx $ sudo mkdir -p /var/log/nginx $ sudo chown nginx:nginx /var/log/nginx $ sudo chmod 755 /var/log/nginx
2.2.3. Nginx
/etc/nginx/nginx.conf:
events { }
http {
server {
listen 80;
server_name conybeare.us:
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name conybeare.us;
ssl_certificate /etc/letsencrypt/live/conybeare.us/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/conybeare.us/privkey.pem;
location /git/ {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
2.2.4. Runtime environment for nginx
Using nix again:
/etc/nginx/nix/flake.nix:
{
description = "Nginx environment";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let pkgs = nixpkgs.legacyPackages.${system};
in {
packages.default = pkgs.nginx;
apps.nginx = {
type = "app";
program = "${pkgs.nginx}/bin/nginx";
};
}
);
}
To verify syntax (and fetch deps)
$ nix flake show /etc/nginx/nix $ ls /etc/nginx/nix/flake.lock
2.2.5. Systemd setup (Nginx)
/etc/systemd/system/nginx.service:
[Unit] Description=Nginx After=network.target [Service] Type=forking # User=nginx # start as root, bind to ports 80/443; then drop to nginx user for workers ExecStart=/nix/var/nix/profiles/default/bin/nix run /etc/nginx/nix#nginx -- -c /etc/nginx/nginx.conf ExecReload=/nix/var/nix/profiles/default/bin/nix run /etc/nginx/nix#nginx -- -c /etc/nginx/nginx.conf -s reload ExecStop=/nix/var/nix/profiles/default/bin/nix run /etc/nginx/nix#nginx -- -s quit [Install] WantedBy=multi-user.target
then ssetup
$ sudo systemctl daemon-reload $ sudo systemctl restart nginx $ sudo systemctl status nginx
$ systemctl status nginx
● nginx.service - Nginx
Loaded: loaded (/etc/systemd/system/nginx.service; disabled; preset: enabled)
Active: active (running) since Mon 2026-05-18 04:13:51 UTC; 4s ago
Process: 1045381 ExecStart=/nix/var/nix/profiles/default/bin/nix run /etc/nginx/nix#nginx -- -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
Main PID: 1045385 (nginx)
Tasks: 2 (limit: 9489)
Memory: 2.9M (peak: 145.4M)
CPU: 1.281s
CGroup: /system.slice/nginx.service
├─1045385 "nginx: master process /nix/store/6xi5jqgw6mqdvzk36285ywc17wnx1548-nginx-1.30.1/bin/nginx -c /etc/nginx/nginx.conf"
└─1045386 "nginx: worker process"
May 18 04:13:50 vpn1 systemd[1]: Starting nginx.service - Nginx...
May 18 04:13:51 vpn1 nix[1045381]: nginx: [warn] the "listen ... http2" directive is deprecated, use the "http2" directive instead in /etc/nginx/nginx.conf:11
May 18 04:13:51 vpn1 systemd[1]: Started nginx.service - Nginx.
2.3. Enable-on-boot
$ sudo systemctl enable nginx forgejo
2.4. Testing
$ curl https://conybeare.us <!DOCTYPE html> <html lang="en-US" data-theme="forgejo-auto"> ...
2.4.1. Forgejo install (web)
Now setup forgejo by visiting https://conybeare.us
| Instance Slogan | It's mad science, all the way down |
| Repository root path | /var/lib/forgejo/gitea-repositories |
| Git LFS root path | /var/lib/forgejo/lfs |
| Log path | /var/log/forgejo |
| Administrator Account | username, email, … |