Files
kubesolo-os/docs/ci-runners.md
Adolfo Delorenzo 059ec7955f chore: housekeeping for v0.3 prep
- Pin KUBESOLO_VERSION in versions.env (was soft-defaulted in fetch-components.sh)
- Gitignore screenshots, macOS resource forks, and common image extensions
- Update README roadmap: x86_64 stable, ARM64 generic in progress (v0.3),
  ARM64 RPi paused pending hardware
- Add docs/ci-runners.md documenting the Odroid arm64-linux Gitea runner

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 09:44:01 -06:00

4.9 KiB

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:

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: <repo>/settings/actions/runners → "Create new Runner"
    • Org-scoped (preferred for this project): <org>/-/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.

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 https://gitea.com/gitea/runner/releases for the current stable tag before bumping.

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_runnergitea-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

./act_runner register --no-interactive \
  --instance https://git.oe74.net \
  --token PASTE_TOKEN_HERE \
  --name <hostname> \
  --labels arm64-linux        # adjust label for amd64 hosts

This creates a .runner file with the registration credentials.

Step 4 — Generate and tune config

./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:

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

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:

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.