Files
grm/docs/user/installation.md
T
emil 12f4aa4c92
Post-merge / configure-repo (push) Waiting to run
Post-merge / publish (push) Waiting to run
Post-merge / detect-type (push) Waiting to run
Post-merge / release (push) Waiting to run
Post-merge / sync-wiki (push) Waiting to run
Post-merge / validate-commit-msg (push) Waiting to run
Post-merge / vikunja (push) Waiting to run
Post-merge / badges (push) Waiting to run
GRM-141: feat: consolidate docs checks into devx-docs-check target
2026-07-07 22:15:25 +00:00

8.5 KiB

Installation

Before you start: Make sure you have cloned the repo and checked out the latest stable release tag. See Getting Started for setup instructions. Do not run from master — it may contain unreleased changes.

Prerequisites

On your local machine (where you run grm)

  • Python 3.12+ — GRM targets Python 3.12 and requires it for development setup. Use pyenv if you need to manage multiple Python versions.
  • Ansible — Installed automatically by make setup (via pip). GRM delegates all remote operations to ansible-playbook.
  • SSH key — A private key that grants access to the target host(s) as a user with sudo privileges.

On the target host(s) (where runners will be installed)

  • SSH server — The remote host must be reachable via SSH using the user specified with --user and the private key specified with --key. GRM uses Ansible under the hood, which connects to the target host over SSH to execute all installation and configuration tasks. Without valid SSH credentials, Ansible cannot establish a connection and the deployment will fail.
  • Sudo access — GRM requires root privileges on the remote host to create system users, install packages, and configure rootless Docker. By default, you will be prompted interactively for the sudo password. For automation or uninterrupted workflows, configure passwordless sudo on the remote host and pass --no-ask-become-pass.
  • Gitea registration token — You need a runner registration token from your Gitea instance. See Getting Started for detailed instructions on obtaining tokens.
  • systemd — Required for user services and lingering. All supported OSes ship with systemd.
  • Docker — Installed automatically by the Ansible role (rootless mode). No pre-existing Docker installation is required.

Supported Operating Systems

OS Versions Package manager
Arch Linux rolling pacman
Ubuntu 22.04, 24.04 apt
Debian 12 apt

All supported OSes are tested in CI via Molecule scenarios on every PR that changes Ansible files. The platform matrix is defined in devx.molecule.platforms as the single source of truth.

Installation Methods

git clone https://git.oblachno.oblachno.fyi/oblachno-oss/grm.git
cd grm
git checkout $(git describe --tags --abbrev=0)  # Latest stable release
make setup
source .venv/bin/activate

make setup performs the following:

  1. Verifies Python 3.12+ is installed
  2. Creates a virtualenv in .venv
  3. Installs all Python dependencies (including Ansible, Click, python-dotenv)
  4. Creates .env from .env.example if not present
  5. Installs development tools (actionlint, git-cliff, act_runner, checkmake)
  6. Sets up pre-commit hooks

Method 2: Via pip

GRM is published to the Gitea PyPI registry at https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple. The registry is publicly readable — no authentication required to install.

Quick install (one-off):

pip install grm --index-url https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple

Persistent configuration (recommended):

Add the registry to ~/.pip/pip.conf:

[global]
extra-index-url = https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple

Then install normally:

pip install grm

This installs the grm CLI and its Python dependencies. The Ansible playbooks and role are bundled with the package, so grm install works out of the box. For development or access to Make targets, clone the repository (Method 1).

Post-install configuration

After installation, create your .env file:

cp .env.example .env
# Edit .env with your Gitea URL and registration token

Required variables:

Variable Description
GITEA_URL Your Gitea instance URL (e.g., https://git.example.com)
GITEA_REGISTRATION_TOKEN Runner registration token from Gitea (starts with GR)

See Getting Started for detailed token setup instructions.

Quick Start Install

Using the CLI (you will be prompted for the sudo password by default):

grm install 192.168.1.10 --user ubuntu --key ~/.ssh/id_ed25519 --name prod-runner

Automation tip: Configure passwordless sudo on the remote host and pass --no-ask-become-pass to skip the password prompt. This is recommended for CI/CD pipelines.

Make Install

Using Make:

make install HOST=192.168.1.10 USER=ubuntu KEY=~/.ssh/id_ed25519 NAME=prod-runner

The Make target wraps the grm install CLI command. All Make install variables are optional except HOST:

Variable Description
HOST Remote host (IP address or hostname) — required
USER SSH user
KEY Path to SSH private key
NAME Gitea Runner name
TOKEN Registration token
ASK_BECOME_PASS Set to 1 to prompt for sudo password

What Gets Installed on the Target Host

When you run grm install, the Ansible role creates the following on the remote host:

Resource Path Description
System user grm-<name> Dedicated system user with /bin/bash shell
Home directory /home/grm-<name>/ User home with .config/systemd/user/
Data directory /var/lib/gitea-runner/<name>/ Runner data including .runner registration file
Config directory /etc/gitea-runner/<name>/ Runner configuration file (config.yaml)
Runner binary /usr/local/bin/gitea_runner The gitea_runner executable
Docker socket /run/user/<UID>/docker.sock Rootless Docker socket
Systemd service gitea-runner.service User service for the runner daemon
Docker prune timer docker-prune.timer Daily Docker cleanup timer
subuid/subgid /etc/subuid, /etc/subgid User namespace mapping (100000-165535)
Lingering loginctl enable-linger Ensures services run without active login

Runner Registry

After installation, 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. This means you rarely need to repeat connection arguments:

# List all registered runners with live systemd status
grm list

# Manage runners by name — connection details come from the registry
grm status prod-runner
grm stop prod-runner
grm start prod-runner

You can override any stored value by passing the corresponding flag (--host, --user, --key).

Registry file format

{
  "prod-runner": {
    "host": "192.168.1.10",
    "user": "ubuntu",
    "key": "/home/user/.ssh/id_ed25519",
    "gitea_url": "https://git.example.com",
    "labels": "docker:docker://gitea/runner-images:ubuntu-latest",
    "created_at": "2026-06-22T14:30:12.000000+00:00"
  }
}

Multiple Instances on the Same Host

Each runner instance is fully isolated with its own system user, rootless Docker daemon, data directory, and systemd user service:

# Install two runners on the same host
grm install 192.168.1.10 --user ubuntu --name workflow-runner
grm install 192.168.1.10 --user ubuntu --name build-runner

# Manage them independently by name
grm stop workflow-runner
grm status build-runner
grm list

Each instance gets:

  • Dedicated system user: grm-<name> with its own home directory
  • Rootless Docker daemon: Isolated Docker socket at /run/user/<UID>/docker.sock
  • Data directory: /var/lib/gitea-runner/<name>/
  • Config directory: /etc/gitea-runner/<name>/
  • Systemd user service: gitea-runner.service (independent start/stop/enable)
  • Docker prune timer: Per-instance daily cleanup

Runners on the same host never interfere with each other or with the host's Docker installation.

Updating Runners

To update the gitea_runner binary on a remote host:

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

Using Make:

make update HOST=192.168.1.10 USER=ubuntu VERSION=1.0.8

Removing Runners

To remove a runner entirely (deregisters from Gitea, removes user, directories, and service files):

grm remove prod-runner --token <registration-token>

To skip remote cleanup and only remove the local registry entry (useful when the remote host is already gone):

grm remove prod-runner --force

Using Make:

make remove NAME=prod-runner TOKEN=<registration-token>