1
FAQ
Devin CI edited this page 2026-08-04 14:04:14 +00:00

FAQ

How to 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 is it needed?

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 won't show the extra API confirmation.

How to 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:

echo "ubuntu ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/grm

Then use:

grm install 192.168.1.10 --user ubuntu --key ~/.ssh/id_ed25519 --name prod-runner --no-ask-become-pass

Can multiple runners run on the same host?

Yes. Each runner instance is fully isolated with its own system user (grm-<name>), rootless Docker daemon, data directory, and systemd user service. Install additional runners with different --name values and manage them independently by name.

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 a 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-<name> 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-<name> systemctl --user status docker
  • Lingering not enabled — check loginctl show-user grm-<name> | 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 to update the gitea_runner binary

Use the grm update command:

grm update 192.168.1.10 --user ubuntu

To update to a specific version:

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 to remove a runner entirely

Use the grm remove command:

grm remove prod-runner --token <registration-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:

grm remove prod-runner --force

What is the difference between disable and remove?

  • grm disable <name> — 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 <name> — Removes the runner entirely: 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.

On Arch Linux, the rootless Docker setup scripts (dockerd-rootless-setuptool.sh, dockerd-rootless.sh) and the rootlesskit runtime are provisioned automatically — Arch's docker package does not ship them. No extra setup is required.

How to 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).

GRM_LANG=bg grm install 192.168.1.10 --user ubuntu --name prod-runner

Or set it in your .env file:

GRM_LANG=bg

How to enable debug logging

Set the GRM_LOG_LEVEL environment variable to DEBUG:

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-<timestamp>.log.

What runner labels should be used?

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:

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 GRM be installed via pip?

Yes:

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.