# CI Runners KubeSolo OS is built and tested on Gitea Actions runners. This document records the runners currently in service and how to register a new one if a host is wiped. ## Active runners | Name | Host | Arch | OS | Labels | Notes | |------|------|------|-----|--------|-------| | `odroid-arm64` | `odroid.local` | aarch64 | Ubuntu 22.04 LTS | `arm64-linux`, `ubuntu-latest`, `ubuntu-24.04`, `ubuntu-22.04` | Native ARM64 builder; 6 cores, 1.8 GB RAM + 4 GB swap; runs as systemd service `act_runner` | ## Workflow targeting ARM64-specific jobs target the Odroid via the `arm64-linux` label: ```yaml jobs: build-arm64: runs-on: arm64-linux steps: - uses: actions/checkout@v4 - run: make rootfs-arm64 ``` Generic ubuntu jobs that don't care about arch fall through to whichever runner picks them up first; on the Odroid they run in Docker via the `ubuntu-latest` / `ubuntu-22.04` / `ubuntu-24.04` labels. ## Registering a new runner ### Prerequisites - Linux host (Ubuntu / Debian preferred; the install instructions below use Ubuntu 22.04+ paths). - Outbound HTTPS to the Gitea instance. - Root access on the runner host (the runner needs to create loop devices and run `mkfs.ext4` for disk-image builds). - A Gitea Actions runner registration token. Get it from: - **Repo-scoped:** `/settings/actions/runners` → "Create new Runner" - **Org-scoped (preferred for this project):** `/-/settings/actions/runners` → "Create new Runner" - **Site-scoped:** `/-/admin/actions/runners` → "Create new Runner" ### Step 1 — Add swap if the host has <4 GB RAM Kernel builds in later phases need ~2 GB resident; tight hosts will OOM-kill `cc1` without swap. ```bash sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab ``` ### Step 2 — Install the gitea-runner binary Pinned to a known-good version. Check for the current stable tag before bumping. ```bash sudo -i mkdir -p /opt/act_runner && cd /opt/act_runner # Bump VERSION to the current stable release as needed VERSION=1.0.3 ARCH=$(uname -m | sed 's/aarch64/arm64/; s/x86_64/amd64/') curl -fL "https://gitea.com/gitea/runner/releases/download/v${VERSION}/gitea-runner-${VERSION}-linux-${ARCH}" \ -o act_runner chmod +x act_runner ./act_runner --version ``` > The upstream project was renamed `act_runner` → `gitea-runner` at the v1.0.0 > release. The release asset filenames use `gitea-runner-*` even though we keep the > local binary named `act_runner` to match this systemd unit. The CLI surface > (`register`, `daemon`, `generate-config`) is unchanged. ### Step 3 — Register against Gitea ```bash ./act_runner register --no-interactive \ --instance https://git.oe74.net \ --token PASTE_TOKEN_HERE \ --name \ --labels arm64-linux # adjust label for amd64 hosts ``` This creates a `.runner` file with the registration credentials. ### Step 4 — Generate and tune config ```bash ./act_runner generate-config > config.yaml ``` In `config.yaml`, confirm the `runner.labels:` block includes the labels you want. The `:host` suffix routes jobs directly to the host (no Docker wrapper) — required for disk-image builds that need loop devices and `mkfs`. Example labels for an arm64 host: ```yaml runner: labels: - "arm64-linux:host" - "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest" - "ubuntu-24.04:docker://docker.gitea.com/runner-images:ubuntu-24.04" - "ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04" ``` ### Step 5 — Install as a systemd service ```bash cat > /etc/systemd/system/act_runner.service << 'EOF' [Unit] Description=Gitea Actions runner After=network-online.target Wants=network-online.target [Service] ExecStart=/opt/act_runner/act_runner daemon --config /opt/act_runner/config.yaml WorkingDirectory=/opt/act_runner User=root Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now act_runner systemctl status act_runner --no-pager ``` ### Step 6 — Verify in Gitea UI Visit the runners page at the scope you registered against. The runner should appear as `Idle` with the labels you configured. ## Removing a runner On the host: ```bash systemctl disable --now act_runner rm -rf /opt/act_runner /etc/systemd/system/act_runner.service systemctl daemon-reload ``` Then delete the runner entry from the Gitea Actions UI so Gitea stops trying to schedule against it. ## Operational notes - The runner stores in-progress job working directories under `/tmp/act_runner` by default. Large disk-image builds may need that path moved to a larger volume — edit `host.workdir_parent:` in `config.yaml`. - Logs are visible via `journalctl -u act_runner -f`. - If a job is interrupted (e.g. host reboot mid-build), the Gitea UI will mark it as failed/cancelled. Re-run from the Actions UI.