Lenovo dedicated GitHub Actions runners
Deployed: 2026-11-03. Host: physical Lenovo ThinkBook 15 G4 IAP (lenovo-lap), Debian 13.7, Lab VLAN 10, switch port 7, 192.168.10.193 / lenovo-lap.home.arpa. This deployment is not a Proxmox VM and does not replace the existing gha-general-01 runner VM.
Inventory
| Purpose | GitHub repository served | Runner source checkout | Container / image | Workspace | Runner label |
|---|---|---|---|---|---|
| Backend | Kart-Chaser/kc-backend-core (private) |
/opt/gha-runners/kc-backend-runner |
kc-backend-runner-local / kc-backend-runner:dev |
/workspace-backend |
kc-backend-runner |
| Infrastructure | Kart-Chaser/kartchaser-infrastructure (private) |
/opt/gha-runners/kc-infra-runner |
kc-infra-runner-local / kc-infra-runner:dev |
/workspace-infra |
kc-infra-runner |
Runner source: kc-backend-runner (private) and kc-infra-runner (private). Both images built and passed make r.preflight and make smoke on 2026-11-02. The runners registered successfully and executed real CI jobs on 2026-11-03. Workspaces are host bind mounts at the same absolute path in the container, owned by UID/GID 1001:1001, to support Docker-outside-of-Docker bind mounts. The host Docker group GID was 989 during image builds; the Makefiles discover it dynamically.
The infrastructure repository's RUNNER_LABELS GitHub Actions variable was updated to ["self-hosted","kc-infra-runner"]. Backend migration was tested under PR #233; check the current merge/CI state before claiming it fully migrated. Do not change workflows with hard-coded labels without reviewing them.
Networking and SSH
- Ethernet:
enp0s31f6, Intel I219-V (e1000e), MACe4:a8:df:dd:bd:29; switch TL-SG108E port 7, VLAN 10 untagged, PVID 10, VLAN 1 not member. ifupdown:/etc/network/interfacesincludes/etc/network/interfaces.d/*;/etc/network/interfaces.d/enp0s31f6containsauto enp0s31f6andiface enp0s31f6 inet dhcp.dhcpcd-baseprovides DHCP. Wired DHCP and SSH successfully returned after reboot.- Router: Flint 2 gateway
192.168.10.1.lenovo-lap.home.arparesolved to192.168.10.193and SSH connected from the XPS at192.168.20.173. - Flint 2 firewall UCI section
firewall.allow_xps_to_lenovo_ssh:src=trusted,src_ip=192.168.20.173,dest=lab,dest_ip=192.168.10.193,proto=tcp,dest_port=22,family=ipv4,target=ACCEPT.fw4 checkand reload passed. This rule does not grant all Trusted clients Lab access. - Open verification: DHCP host reservation for MAC
e4:a8:df:dd:bd:29/192.168.10.193was not independently shown in router output. Verify it before relying on this fixed-IP firewall rule long term.
ssh pooltab@lenovo-lap.home.arpa
ip -4 addr show enp0s31f6
ip -4 route
systemctl is-active networking ssh docker containerd
Systemd and Docker startup
Docker Engine 29.9.2 and containerd are enabled. Both containers use Docker --restart unless-stopped. The services are:
gha-runner@kc-backend-runner-local.servicegha-runner@kc-infra-runner-local.service
Both instances are enabled and were observed active on 2026-11-03. The shared template lives at /etc/systemd/system/gha-runner@.service; the executable helper is /usr/local/libexec/gha-runner-start.
This is a Type=oneshot, RemainAfterExit=yes service running as host user pooltab with supplementary Docker group. ExecStart checks for an existing container and runs docker start only when stopped; it does not rebuild, replace, or re-register containers. ExecStop calls docker stop -t 120. An active (exited) systemd status is expected and does not itself prove the container or GitHub runner is online. Docker independently handles container process restarts.
systemctl is-enabled gha-runner@kc-backend-runner-local.service gha-runner@kc-infra-runner-local.service
systemctl status gha-runner@kc-backend-runner-local.service gha-runner@kc-infra-runner-local.service --no-pager
docker ps --filter label=kc.runner
docker inspect kc-backend-runner-local kc-infra-runner-local --format '{{.Name}}: {{.State.Status}}, restart={{.HostConfig.RestartPolicy.Name}}'
docker logs --tail 60 kc-backend-runner-local
docker logs --tail 60 kc-infra-runner-local
Query live registration as pooltab with GitHub CLI authenticated for both repositories:
gh api repos/Kart-Chaser/kc-backend-core/actions/runners --jq '.runners[] | select(.name=="kc-backend-runner-local") | {name,status,busy,labels:[.labels[].name]}'
gh api repos/Kart-Chaser/kartchaser-infrastructure/actions/runners --jq '.runners[] | select(.name=="kc-infra-runner-local") | {name,status,busy,labels:[.labels[].name]}'
gh api repos/Kart-Chaser/kartchaser-infrastructure/actions/variables/RUNNER_LABELS --jq '.value'
Safe update procedure
Never run make r.up, stop, restart, or recreate containers while jobs are running. make r.up rebuilds, obtains a new repository registration token and forcibly replaces its named container. Runner labels can immediately receive new jobs; coordinate an assignment pause/drain and verify busy: false before maintenance. The systemd service itself is not job-aware.
For the runner being maintained, after draining jobs:
# BACKEND EXAMPLE ONLY. Never run these while jobs are executing.
sudo systemctl stop gha-runner@kc-backend-runner-local.service
git -C /opt/gha-runners/kc-backend-runner status --short
git -C /opt/gha-runners/kc-backend-runner pull --ff-only origin main
make -C /opt/gha-runners/kc-backend-runner r.preflight
make -C /opt/gha-runners/kc-backend-runner r.up
sudo systemctl start gha-runner@kc-backend-runner-local.service
docker logs --tail 40 kc-backend-runner-local
Repeat separately for infrastructure with its checkout, container and service names. Re-verify GitHub registration after updating; do not run Docker-wide pruning commands on this host. GitHub CLI registration requires admin permission on each served repository. Do not expose registration tokens or GitHub CLI credential files.
Security: The runners mount /var/run/docker.sock and use host networking. Docker socket access effectively grants host-level power to executing workflows, even though registration is repository-scoped. Restrict workflow permissions, contributors and secrets appropriately.
Operational status and follow-ups
- Backend jobs observed as successful on the Lenovo include SDK Package, Label Pull Request and Quality Suite; Container Integration and Workflow Narrator failed during PR #233 testing and were being investigated by another agent. Do not assume they remain failing or have been resolved without checking.
- Backend runner binary version
2.338.1attempted to self-update to2.338.2and failed withCould not find a part of the path '/workspace-backend/_update/runner1.tar.gz'. The workspace cleanup hooks may have contributed, but the cause is unconfirmed. Investigate/update the runner repository in a PR before changing live deployment. - Infrastructure runner auto-updated successfully to
2.338.2and executed Label Pull Request, Trivy misconfiguration scan and Terraform Detect successfully; additional jobs were running at the last captured log. The operator later reported both runners working as expected. - Add monitoring to the existing
monitor-01Prometheus/Grafana stack (node-exporter, container metrics, and runner-specific job/online status). These monitors have not been shown installed on Lenovo. - Confirm DHCP reservation, verify lid-closed operation on this host if necessary, and consider a maintenance procedure that drains jobs before Docker/systemd restarts.