org-howto/articles/2026/05/setup-forgejo-runner.org

12 KiB

[23may2026] setting up forgjo runner on a cloud host

#

#

#

#

#

#

#

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

Plan

Forgejo Runner

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 ];
          };
        }
      );
  }

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.

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

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

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

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

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

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

Proxy Magic

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

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

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;
          }
      }
  }

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

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.

Enable-on-boot

$ sudo systemctl enable nginx forgejo

Testing

$ curl  https://conybeare.us
<!DOCTYPE html>
<html lang="en-US" data-theme="forgejo-auto">
...

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, …