diff --git a/README.md b/README.md index 9d56206..77b65c7 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ None of these nail the "your dad can set this up" experience. The installer wiza - [x] Competitor analysis — see [docs/competitors.md](docs/competitors.md) - [x] Wizard flow spec — see [docs/wizard-flow.md](docs/wizard-flow.md) - [x] Release process + CI — CalVer tags, conventional commits, Forgejo Actions (ruff, pytest, JSON, link checks), `26.0-alpha` tagged -- [ ] **Stand up Forgejo runner** — see [docs/runner-setup.md](docs/runner-setup.md) *(Daniel, next session — without this CI queues forever)* +- [x] Forgejo runner live on Proxmox VM (`forge-runner-01`, Ubuntu 24.04, Docker + DinD sidecar) — setup captured in [docs/runner-setup.md](docs/runner-setup.md) + [ops/forgejo-runner/](ops/forgejo-runner/) - [ ] **Publish `26.0-alpha` Forgejo Release** — [releases/new](https://forgejo.sourcegate.online/daniel/homebase/releases/new), paste CHANGELOG section, tick Pre-release *(Daniel, next session)* - [ ] **Base OS bootable image** — Robert gets a minimal Arch image that boots, runs Docker, serves the installer webapp at `https://proksi.local` *(next blocker)* - [ ] Installer wizard screens S5–S8 (domain, SSL, diagnostic, confirm) diff --git a/docs/runner-setup.md b/docs/runner-setup.md index f93a45b..bbc3172 100644 --- a/docs/runner-setup.md +++ b/docs/runner-setup.md @@ -4,6 +4,8 @@ How to stand up a `forgejo-runner` so the CI workflow in `.forgejo/workflows/ci. The runner is a long-running daemon that polls the Forgejo instance for queued jobs and runs them in Docker containers. +A ready-to-use bootstrap script and compose file live under [`ops/forgejo-runner/`](../ops/forgejo-runner/). + ## Choosing a host | Option | Good for | Trade-off | @@ -20,32 +22,9 @@ Pick either the binary or the Docker container path. Docker is easier to upgrade ### Path A: Docker Compose (recommended) -On the chosen host, create `~/forgejo-runner/compose.yml`: +Copy `ops/forgejo-runner/compose.yml` and `ops/forgejo-runner/config.yml` from this repo to the host, e.g. into `~/forgejo-runner/` (compose file) and `~/forgejo-runner/data/` (config file). The runner talks to a sidecar Docker-in-Docker container via `tcp://docker-in-docker:2375`, so the host's own Docker socket is not exposed to jobs. -```yaml -services: - runner: - image: code.forgejo.org/forgejo/runner:6 - container_name: forgejo-runner - restart: unless-stopped - environment: - - CONFIG_FILE=/data/config.yml - volumes: - - ./data:/data - - /var/run/docker.sock:/var/run/docker.sock - depends_on: - - docker-in-docker - command: /bin/sh -c "sleep 5; forgejo-runner daemon" - - docker-in-docker: - image: docker:dind - container_name: forgejo-runner-dind - restart: unless-stopped - privileged: true - environment: - - DOCKER_TLS_CERTDIR= - command: dockerd -H tcp://0.0.0.0:2375 --tls=false -``` +If the host is a fresh Ubuntu VM, run `ops/forgejo-runner/bootstrap.sh` first to install Docker Engine + the Compose plugin from the official repo. ### Path B: Binary @@ -61,22 +40,24 @@ sudo mv forgejo-runner-6.0.0-linux-amd64 /usr/local/bin/forgejo-runner 1. In the Forgejo web UI: go to **Site Administration → Actions → Runners → Create new Runner**. Copy the registration token. (For a repo-scoped runner instead, use **Repo Settings → Actions → Runners**.) -2. Register from the runner host: +2. Register from the runner host by running the registration inside a one-shot container so the output lands in the mounted `data/` directory: ```bash - forgejo-runner register \ - --instance https://forgejo.sourcegate.online \ - --token \ - --name homebase-runner-1 \ - --labels docker,ubuntu-latest,self-hosted \ - --no-interactive + cd ~/forgejo-runner + docker run --rm -v "$PWD/data:/data" code.forgejo.org/forgejo/runner:6 \ + forgejo-runner register \ + --instance https://forgejo.sourcegate.online \ + --token \ + --name forge-runner-01 \ + --labels 'docker:docker://catthehacker/ubuntu:act-latest,ubuntu-latest:docker://catthehacker/ubuntu:act-latest,self-hosted:docker://catthehacker/ubuntu:act-latest' \ + --no-interactive ``` - This writes `.runner` (registration file) and `config.yml` into the current directory. + Labels *must* use the `:docker://` form — bare labels (`ubuntu-latest`) get stored as `ubuntu-latest:host`, which tells the runner to execute jobs directly inside the runner container (no Python, no git, nothing). `catthehacker/ubuntu:act-latest` is the common drop-in image with GitHub Actions tooling preinstalled. -3. Start the daemon (Docker: `docker compose up -d`; binary: `forgejo-runner daemon`). +3. Start the daemon: `docker compose up -d`. -4. Verify the runner shows up as **Idle** in Forgejo's admin Runners page. +4. Verify the runner shows up as **Idle** in Forgejo's admin Runners page and the log prints `runner: forge-runner-01, ..., declared successfully`. ## First CI run @@ -108,6 +89,7 @@ Save as `/etc/systemd/system/forgejo-runner.service`, then `sudo systemctl enabl ## Security notes -- The runner mounts `/var/run/docker.sock` which gives it root-equivalent access to the host. Run it on a machine you trust and nothing sensitive. +- Jobs run inside a Docker-in-Docker sidecar, not against the host's Docker socket. Still, DinD runs privileged — give the runner its own VM, not a shared host. - Registration tokens are one-shot; a stolen token can't re-register after the runner is live. - Prefer repo-scoped runners over instance-wide if you're sharing the runner with other repos you don't control. +- Ubuntu's default systemd-resolved makes the host's stub resolver (`127.0.0.53`) inherit a LAN DNS server that Docker containers may not be able to reach. If container DNS fails, set explicit upstream DNS in `/etc/docker/daemon.json` (e.g. `{"dns": ["1.1.1.1", "8.8.8.8"]}`) and `sudo systemctl restart docker`. diff --git a/ops/forgejo-runner/bootstrap.sh b/ops/forgejo-runner/bootstrap.sh new file mode 100755 index 0000000..133719c --- /dev/null +++ b/ops/forgejo-runner/bootstrap.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Install Docker Engine + Compose plugin on a fresh Ubuntu 24.04 VM +# and prepare it to host a Forgejo Actions runner. +# +# Run as the target user (needs sudo). Idempotent. +set -euo pipefail + +if [[ "$(. /etc/os-release && echo "$ID")" != "ubuntu" ]]; then + echo "This script targets Ubuntu. Aborting." >&2 + exit 1 +fi + +echo "==> Updating apt and installing prerequisites" +sudo apt-get update -y +sudo apt-get install -y ca-certificates curl gnupg + +echo "==> Adding Docker's official GPG key" +sudo install -m 0755 -d /etc/apt/keyrings +if [[ ! -f /etc/apt/keyrings/docker.asc ]]; then + sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc + sudo chmod a+r /etc/apt/keyrings/docker.asc +fi + +echo "==> Adding Docker apt repository" +ARCH="$(dpkg --print-architecture)" +CODENAME="$(. /etc/os-release && echo "$VERSION_CODENAME")" +echo "deb [arch=${ARCH} signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu ${CODENAME} stable" \ + | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + +echo "==> Installing Docker Engine + Compose plugin" +sudo apt-get update -y +sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + +echo "==> Adding $USER to docker group" +sudo usermod -aG docker "$USER" + +echo "==> Enabling docker service" +sudo systemctl enable --now docker + +echo +echo "Done. Log out and back in (or run 'newgrp docker') so group membership takes effect." +docker --version +docker compose version diff --git a/ops/forgejo-runner/compose.yml b/ops/forgejo-runner/compose.yml new file mode 100644 index 0000000..faf6c66 --- /dev/null +++ b/ops/forgejo-runner/compose.yml @@ -0,0 +1,22 @@ +services: + runner: + image: code.forgejo.org/forgejo/runner:6 + container_name: forgejo-runner + restart: unless-stopped + environment: + - DOCKER_HOST=tcp://docker-in-docker:2375 + - CONFIG_FILE=/data/config.yml + volumes: + - ./data:/data + depends_on: + - docker-in-docker + command: /bin/sh -c "sleep 5; forgejo-runner daemon --config /data/config.yml" + + docker-in-docker: + image: docker:dind + container_name: forgejo-runner-dind + restart: unless-stopped + privileged: true + environment: + - DOCKER_TLS_CERTDIR= + command: dockerd -H tcp://0.0.0.0:2375 --tls=false diff --git a/ops/forgejo-runner/config.yml b/ops/forgejo-runner/config.yml new file mode 100644 index 0000000..849ed2c --- /dev/null +++ b/ops/forgejo-runner/config.yml @@ -0,0 +1,30 @@ +log: + level: debug + job_level: info + +runner: + file: .runner + capacity: 1 + timeout: 3h + insecure: false + fetch_timeout: 5s + fetch_interval: 2s + report_interval: 1s + labels: [] + +cache: + enabled: true + dir: "" + host: "" + port: 0 + proxy_port: 0 + +container: + network: "" + privileged: false + valid_volumes: [] + docker_host: "tcp://docker-in-docker:2375" + force_pull: false + +host: + workdir_parent: