# FAQ ## How do I obtain the Gitea registration token? There are three levels of registration tokens, depending on which repositories the runner should serve: - **Instance-level** — Site Administration → Actions → Runners → Create Registration Token. The runner will handle jobs from all repositories. - **Organization-level** — Organization → Settings → Actions → Runners → Create Registration Token. The runner will only handle jobs from repositories in that organization. - **Repository-level** — Repository → Settings → Actions → Runners → Create Registration Token. The runner will only handle jobs from that specific repository. Set the token as `GITEA_REGISTRATION_TOKEN` in your `.env` file or pass it via `--token` on the command line. ## What is the CI_GITEA_TOKEN and do I need it? `CI_GITEA_TOKEN` is a Gitea admin API token used for optional post-install verification. When set, GRM queries the Gitea API after installation to confirm the runner appears in the runner list. This is purely informational — the integration test passes/fails based on the `.runner` file and systemd service, not the API check. To generate one: Settings → Applications → Generate New Token, with the `admin` scope (or at minimum `read:user`, `read:repository`, `read:admin`). If you skip it, GRM will still verify the runner correctly — it just won't show the extra API confirmation. ## How do I skip the sudo password prompt for automation? Configure passwordless sudo on the remote host and pass `--no-ask-become-pass` to the CLI command. This is recommended for CI/CD pipelines. On the remote host, add a sudoers entry: ```bash echo "ubuntu ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/grm ``` Then use: ```bash grm install 192.168.1.10 --user ubuntu --key ~/.ssh/id_ed25519 --name prod-runner --no-ask-become-pass ``` ## Can I run multiple runners on the same host? Yes. Each runner instance is fully isolated with its own system user (`grm-`), rootless Docker daemon, data directory, and systemd user service. Install additional runners with different `--name` values and manage them independently by name. ```bash grm install 192.168.1.10 --user ubuntu --name workflow-runner grm install 192.168.1.10 --user ubuntu --name build-runner grm list ``` Runners on the same host never interfere with each other or with the host's Docker installation. ## Why does my runner appear offline after installation? Check that `GITEA_URL` and `GITEA_REGISTRATION_TOKEN` are correct, verify the runner service is running with `sudo -u grm- systemctl --user status gitea-runner`, and check the logs for registration errors. You can also confirm the runner appears as **Online** in the Gitea UI under **Actions → Runners**. Common causes: - Registration token expired — generate a new one from Gitea - Network connectivity issue between the runner host and Gitea - Rootless Docker daemon not running — check `sudo -u grm- systemctl --user status docker` - Lingering not enabled — check `loginctl show-user grm- | grep Linger` ## What does the "Event loop is closed" warning mean? This is a harmless cleanup traceback from Molecule's Docker driver when the test process is interrupted. It does not indicate a test failure. ## Where are runner connection details stored? GRM stores each runner's connection details (host, user, SSH key, Gitea URL, labels) in a local JSON registry at `~/.local/share/grm/runners.json`. After installation, lifecycle commands work by runner name only — you can override any stored value by passing the corresponding flag. ## How do I update the gitea_runner binary? Use the `grm update` command: ```bash grm update 192.168.1.10 --user ubuntu ``` To update to a specific version: ```bash grm update 192.168.1.10 --user ubuntu --version 1.0.8 ``` The update command downloads the new binary and replaces the existing one at `/usr/local/bin/gitea_runner`. The runner service is restarted automatically. ## How do I completely remove a runner? Use the `grm remove` command: ```bash grm remove prod-runner --token ``` This deregisters the runner from Gitea, stops and disables the systemd service, removes the system user, deletes data and config directories, removes subuid/subgid entries, disables lingering, and removes the entry from the local registry. If the remote host is already gone or unreachable, use `--force` to skip remote cleanup and only remove the local registry entry: ```bash grm remove prod-runner --force ``` ## What is the difference between disable and remove? - **`grm disable `** — Deregisters the runner from Gitea and stops the service, but leaves the user, directories, and service files in place. The runner can be re-enabled later with `grm enable` and re-registered with a new token. - **`grm remove `** — Completely removes the runner: deregisters from Gitea, stops and disables the service, removes the system user, deletes all directories, and removes the local registry entry. This is irreversible. ## What operating systems are supported? GRM supports Arch Linux (rolling), Ubuntu 22.04/24.04, and Debian 12. All supported OSes are tested in CI via Molecule scenarios on every PR that changes Ansible files. ## How do I change the UI language? Set the `GRM_LANG` environment variable to one of the supported languages: `en` (English, default), `bg` (Bulgarian), `de` (German), `ru` (Russian), `zh` (Chinese), `pl` (Polish). ```bash GRM_LANG=bg grm install 192.168.1.10 --user ubuntu --name prod-runner ``` Or set it in your `.env` file: ```bash GRM_LANG=bg ``` ## How do I enable debug logging? Set the `GRM_LOG_LEVEL` environment variable to `DEBUG`: ```bash GRM_LOG_LEVEL=DEBUG grm install 192.168.1.10 --user ubuntu --name prod-runner ``` The log file at `~/.local/state/grm/logs/grm.log` always captures DEBUG level regardless of this setting. Ansible execution logs are stored in timestamped files at `~/.local/state/grm/logs/ansible-.log`. ## What runner labels should I use? By default, runners are registered with `docker,ubuntu-latest:docker://runner-images:ubuntu-22.04`. You can override this with `--labels` or the `GITEA_RUNNER_LABELS` environment variable. Use an official Gitea runner image with Node.js, Python, and Docker CLI. Avoid bare OS images like `alpine:latest` because `actions/checkout@v4` needs Node.js. Example: ```bash grm install 192.168.1.10 --user ubuntu --name prod-runner \ --labels "docker:docker://gitea/runner-images:ubuntu-latest" ``` ## Is GRM secure? Yes. GRM is designed with security as a first-class concern: - **Rootless Docker**: Each runner operates under a dedicated unprivileged system user. Containers never have root access to the host. - **Secret handling**: Registration tokens are passed via temporary JSON files with `0600` permissions, never on the command line (CWE-214). - **No shell injection**: The CLI never uses `shell=True` with subprocess. - **Bandit security scan**: The CI pipeline runs Bandit on every PR. ## Can I install GRM via pip? Yes: ```bash pip install grm ``` This installs the `grm` CLI and its Python dependencies. The Ansible playbooks and role are bundled with the package. For development or access to Make targets, clone the repository instead. ## How does GRM handle idempotence? The Ansible role is idempotent — running `grm install` twice produces zero changes on the second run. Each task checks for existing state before making changes. For example: - User creation uses `ansible.builtin.user` which only creates if the user doesn't exist - Package installation uses `state: present` which only installs if not already installed - Template creation uses `ansible.builtin.template` which only writes if the content changed - Rootless Docker setup uses `creates:` to skip if already configured This makes GRM safe for CI/CD pipelines and configuration management.