Files
homey/docs/gitea-runner-workflows.org
2026-05-10 11:30:43 +03:00

9.1 KiB

Gitea Actions Runner — Workflows & Usage Guide

Overview

This document covers the Gitea Actions runner setup on pi-main, how the runner works, the label system, and example workflows for both host-based ("ubuntu") and Nix-native jobs.

Architecture

The runner is configured in modules/services/gitea-runner.nix and uses the NixOS native services.gitea-actions-runner module. Jobs run with the host executor: each step executes directly on the Pi 4 as the gitea-runner-pi-main system user. There is no container isolation per job.

Gitea (podman container)
  │  HTTPS  →  Cloudflare tunnel  →  Caddy  →  git.zakobar.com
  │  (runner connects outbound via HTTPS, same path as a browser)
  ▼
gitea-actions-runner (systemd service)
  │  host executor
  ▼
Jobs run as: gitea-runner-pi-main  (unprivileged system user)
  PATH includes: nix, git, bash + system packages

Runner labels

Labels are advertised to Gitea and matched against runs-on: in workflow files. The default labels configured in this project are:

Label Executor Notes
native:host host Canonical label for "run on this machine"
ubuntu-latest host Matches common GitHub Actions workflows
debian-latest host Alternative for Debian-targeting workflows
nix:host host Explicit label for Nix-native jobs

All four labels route to the same runner process and the same host environment. The difference is purely semantic — pick the label that makes your workflow's intent clear.

Nix daemon trust

The runner user is added to nix.settings.trusted-users, which means it can:

  • Evaluate flakes (nix flake check, nix build)
  • Write derivation outputs to the Nix store
  • Pass --extra-experimental-features flags to the daemon
  • Use nix copy to push/pull store paths to a remote cache

It cannot modify NixOS system configuration or run privileged operations.

Example workflows

Workflow files live in .gitea/workflows/ inside each repository (or .github/workflows/ — Gitea Actions supports both paths).

Minimal smoke test (host)

The simplest possible workflow — runs a shell command on the runner.

# .gitea/workflows/smoke.yaml
on: [push]
jobs:
  smoke:
    runs-on: native:host
    steps:
      - uses: actions/checkout@v3
      - run: echo "Runner is alive on $(hostname)"

Standard shell-based CI (ubuntu-latest label)

Use this for repos that want to stay compatible with GitHub Actions. The workflow looks identical to what you'd push to GitHub; it just runs on your Pi.

# .gitea/workflows/ci.yaml
on:
  push:
    branches: [main, master]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install dependencies
        run: |
          # On the host executor, use nix-shell or system packages.
          # apt/yum are NOT available — this is NixOS, not Ubuntu.
          # Use nix-shell -p for one-off tools:
          nix-shell -p nodejs --run "node --version"

      - name: Run tests
        run: |
          nix-shell -p nodejs --run "npm test"

Important: Despite the label ubuntu-latest, the host is NixOS. apt, yum, and FHS paths like /usr/bin/python3 are not available. Use nix-shell -p <pkg> to bring in any tool you need.

Nix flake check

Validate a flake on every push — the most common use case for this runner.

# .gitea/workflows/flake-check.yaml
on: [push, pull_request]
jobs:
  check:
    runs-on: nix:host
    steps:
      - uses: actions/checkout@v3

      - name: Check flake
        run: nix flake check --no-build

      - name: Build default package
        run: nix build

Nix build with caching

Build a derivation and push the result to a binary cache so subsequent builds are fast. Requires a Cachix account or an S3-compatible cache configured in nix.settings.

# .gitea/workflows/build-and-cache.yaml
on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: nix:host
    steps:
      - uses: actions/checkout@v3

      - name: Build
        run: nix build --print-build-logs

      - name: Push to cache
        # nix copy requires the runner user to be in trusted-users (already set).
        # Replace the URI with your actual cache.
        run: |
          nix copy --to "s3://your-cache-bucket?region=us-east-1" ./result

NixOS configuration check (this repo)

Check that the homey flake evaluates cleanly on every change. Add this to the homey repo itself.

# .gitea/workflows/nixos-check.yaml
on: [push, pull_request]
jobs:
  eval:
    runs-on: nix:host
    steps:
      - uses: actions/checkout@v3

      - name: Evaluate NixOS configurations
        run: |
          nix flake check --no-build
          # Optionally build a specific host config (slow on Pi):
          # nix build .#nixosConfigurations.pi-main.config.system.build.toplevel

      - name: Check formatting (optional)
        run: |
          nix-shell -p nixpkgs-fmt --run "nixpkgs-fmt --check ."

Multi-step pipeline with artifacts

# .gitea/workflows/pipeline.yaml
on:
  push:
    tags: ['v*']

jobs:
  build:
    runs-on: nix:host
    steps:
      - uses: actions/checkout@v3

      - name: Build release
        run: nix build --out-link result

      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          name: release-binary
          path: result/bin/

  deploy:
    needs: build
    runs-on: native:host
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v3
        with:
          name: release-binary

      - name: Deploy
        run: ./deploy.sh

Caveats and gotchas

No apt/brew/yum

The host is NixOS. Package managers from other distros do not work. Use nix-shell -p <pkg> --run "..." for ad-hoc tools, or add a shell.nix / flake.nix devShell to your repo and enter it with nix develop.

No Docker/Podman per job

The host executor does not launch a fresh container per job. All jobs share the same filesystem (under /home/gitea-runner-pi-main/) and the same running system. This means:

  • No isolation between concurrent jobs (though concurrency defaults to 1).
  • Side effects (files written, packages installed with nix) persist between runs unless you clean up explicitly.
  • Use nix build output symlinks (./result) rather than writing to system paths.

actions/checkout and git

The actions/checkout@v3 action works fine on the host executor. It clones into the runner's working directory. Subsequent steps run in that directory by default.

If you use actions/checkout@v4, note that it requires a newer Node.js. On NixOS you can't rely on a system Node, so either pin to v3 or use:

      - uses: actions/checkout@v3   # v3 bundles its own Node runtime

Nix experimental features

Flake commands require nix-command and flakes experimental features. These are typically enabled system-wide in nix.settings.experimental-features in modules/common.nix. If a job fails with "experimental feature not enabled", you can pass it inline:

      - run: nix --extra-experimental-features "nix-command flakes" flake check

Or ensure common.nix has:

nix.settings.experimental-features = [ "nix-command" "flakes" ];

Token rotation

The registration token in gitea/runner_token is consumed on first registration. The runner then stores its own credentials in /var/lib/gitea-runner/pi-main/.runner. If you need to re-register (e.g. after wiping the state directory), generate a new token from Gitea's admin UI and update the sops secret before restarting the service.

Pi 4 performance

The Pi 4 is capable but not fast for heavy builds. Tips:

  • Enable the Nix binary cache (nixos-cache.nixos.org is on by default) so pre-built derivations are fetched instead of compiled.
  • Set nix.settings.max-jobs to 4 to use all cores for parallel builds.
  • Avoid building large packages (LLVM, Chromium) locally — push to a remote builder or use Cachix.

Debugging

Check runner status

systemctl status gitea-runner-pi-main
journalctl -u gitea-runner-pi-main -f

Runner registration state

cat /var/lib/gitea-runner/pi-main/.runner

Force re-registration

# Stop, wipe state, restart (runner will re-register using the token file)
systemctl stop gitea-runner-pi-main
rm /var/lib/gitea-runner/pi-main/.runner
systemctl start gitea-runner-pi-main

Test a workflow locally

Use act (the local runner) to test workflow files before pushing:

nix-shell -p act --run "act push"

Note: act spins up Docker containers for each job, so results may differ slightly from the host-executor runner, but it is useful for syntax checking and logic testing.