v0.5.0 22211fd

README.md RawHistoryFolder

Read-only mirror. Development happens at git.bnema.dev; issues and pull requests are closed here.


ermite-runner fetches jobs from an ermite server and runs workflows written for GitHub Actions (.github/workflows/*.yml). Each job runs in its own sandbox, destroyed when the job ends.

Isolation modes

Mode Sandbox Needs
vm A Firecracker microVM per job A usable /dev/kvm
container A container per job with its own Docker daemon A rootful Docker Engine

ERMITE_RUNNER_ISOLATION=auto is the default. It picks vm when /dev/kvm is usable and Firecracker, the guest kernel and the base image exist. Otherwise it picks container. The chosen mode and the reasons are logged at start.

Details: docs/sandboxes.md.

Requirements

  • A Linux host. The runner runs as root.
  • ip, nft and mkfs.ext4 (e2fsprogs 1.43 or newer).
  • Go, to build the runner.
  • For vm: /dev/kvm, firecracker and jailer in PATH, a guest kernel and a base image.
  • For container: a rootful Docker Engine and the job image. sysbox is optional: without it the runner uses a privileged container.

docs/images.md explains how to build the kernel and the images.

Build

The binary must be static:

CGO_ENABLED=0 go build -trimpath -o ermite-runner .

Register and run

  1. On the ermite server, create a registration token:

    ermite token create my-runner --preset runner
    
  2. Install the binary, the unit and the settings:

    sudo install -m 0755 ermite-runner /usr/local/bin/ermite-runner
    sudo install -m 0644 contrib/ermite-runner.service /etc/systemd/system/
    sudo install -d -m 0700 /etc/ermite-runner
    sudo install -m 0600 /dev/stdin /etc/ermite-runner/env <<'ENV'
    ERMITE_RUNNER_URL=https://ermite.example.com
    ERMITE_RUNNER_NAME=runner-1
    ERMITE_RUNNER_LABELS=ubuntu-latest,ubuntu-26.04
    ERMITE_RUNNER_LOG_FORMAT=json
    ENV
    
  3. Register once. The token goes through standard input, so it is in no command line:

    printf %s "$TOKEN" | sudo systemd-run --pipe --wait \
      -p EnvironmentFile=/etc/ermite-runner/env \
      -p StateDirectory=ermite-runner -p StateDirectoryMode=0700 \
      /usr/local/bin/ermite-runner register --token-stdin
    
  4. Start the service:

    sudo systemctl enable --now ermite-runner
    journalctl -u ermite-runner -f
    

ermite-runner run polls the server for jobs and runs each one in a sandbox, up to ERMITE_RUNNER_CAPACITY jobs at a time. On SIGTERM it cancels the running jobs, reports them and removes their sandboxes.

To check an installation without a server, run ermite-runner sandbox-check. It runs one stub script in the selected mode.

More on the service, logs and shutdown: docs/service.md.

Commands

Command Purpose
ermite-runner register [--token-stdin] [--token T] [--replace] Register with the server and store the runner token
ermite-runner run Fetch and run jobs
ermite-runner sandbox-check [script] Run one stub script in a sandbox
ermite-runner version Print the version

Key settings

Variable Default Meaning
ERMITE_RUNNER_URL Base URL of the ermite server. Must be https://
ERMITE_RUNNER_NAME host name Runner name
ERMITE_RUNNER_LABELS Extra labels, comma separated
ERMITE_RUNNER_ISOLATION auto auto, vm or container
ERMITE_RUNNER_CAPACITY 1 Jobs in parallel
ERMITE_RUNNER_JOB_CPUS 2 CPUs per job
ERMITE_RUNNER_JOB_MEMORY 4G Memory per job
ERMITE_RUNNER_JOB_DISK 64G Scratch disk per job
ERMITE_RUNNER_STATE_DIR /var/lib/ermite-runner State directory

All settings: docs/configuration.md.

Security notes

  • Run only one runner per host.
  • The registration token can read the code and secrets of every repository. Pass it through standard input.
  • Without sysbox, container mode uses a privileged container, which is equivalent to host root. Set ERMITE_RUNNER_DENY_PRIVILEGED=1 to refuse it.
  • The state directory and all its parents must be owned by root and not writable by others.

Documentation

License

MIT. See LICENSE. Third-party attributions: THIRD_PARTY.md.

source
1<h1 align="center">ermite-runner</h1>
2
3<p align="center">
4  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="License: MIT"></a>
5  <a href="https://github.com/bnema/ermite-runner/commits/main"><img src="https://badgen.net/github/last-commit/bnema/ermite-runner/main?icon=github" alt="Last commit"></a>
6  <a href="https://github.com/bnema/ermite-runner/stargazers"><img src="https://badgen.net/github/stars/bnema/ermite-runner?icon=github" alt="GitHub stars"></a>
7</p>
8
9<p align="center">The CI runner of <a href="https://github.com/bnema/ermite">ermite</a>: runs GitHub Actions workflows, one microVM per job.</p>
10
11> Read-only mirror. Development happens at git.bnema.dev; issues and pull requests are closed here.
12
13---
14
15ermite-runner fetches jobs from an ermite server and runs workflows written for GitHub Actions (`.github/workflows/*.yml`). Each job runs in its own sandbox, destroyed when the job ends.
16
17## Isolation modes
18
19| Mode | Sandbox | Needs |
20| --- | --- | --- |
21| `vm` | A Firecracker microVM per job | A usable `/dev/kvm` |
22| `container` | A container per job with its own Docker daemon | A rootful Docker Engine |
23
24`ERMITE_RUNNER_ISOLATION=auto` is the default. It picks `vm` when `/dev/kvm` is usable and Firecracker, the guest kernel and the base image exist. Otherwise it picks `container`. The chosen mode and the reasons are logged at start.
25
26Details: [docs/sandboxes.md](docs/sandboxes.md).
27
28## Requirements
29
30- A Linux host. The runner runs as root.
31- `ip`, `nft` and `mkfs.ext4` (e2fsprogs 1.43 or newer).
32- Go, to build the runner.
33- For `vm`: `/dev/kvm`, `firecracker` and `jailer` in `PATH`, a guest kernel and a base image.
34- For `container`: a rootful Docker Engine and the job image. [sysbox](https://github.com/nestybox/sysbox) is optional: without it the runner uses a privileged container.
35
36[docs/images.md](docs/images.md) explains how to build the kernel and the images.
37
38## Build
39
40The binary must be static:
41
42```sh
43CGO_ENABLED=0 go build -trimpath -o ermite-runner .
44```
45
46## Register and run
47
481. On the ermite server, create a registration token:
49
50   ```sh
51   ermite token create my-runner --preset runner
52   ```
53
542. Install the binary, the unit and the settings:
55
56   ```sh
57   sudo install -m 0755 ermite-runner /usr/local/bin/ermite-runner
58   sudo install -m 0644 contrib/ermite-runner.service /etc/systemd/system/
59   sudo install -d -m 0700 /etc/ermite-runner
60   sudo install -m 0600 /dev/stdin /etc/ermite-runner/env <<'ENV'
61   ERMITE_RUNNER_URL=https://ermite.example.com
62   ERMITE_RUNNER_NAME=runner-1
63   ERMITE_RUNNER_LABELS=ubuntu-latest,ubuntu-26.04
64   ERMITE_RUNNER_LOG_FORMAT=json
65   ENV
66   ```
67
683. Register once. The token goes through standard input, so it is in no command line:
69
70   ```sh
71   printf %s "$TOKEN" | sudo systemd-run --pipe --wait \
72     -p EnvironmentFile=/etc/ermite-runner/env \
73     -p StateDirectory=ermite-runner -p StateDirectoryMode=0700 \
74     /usr/local/bin/ermite-runner register --token-stdin
75   ```
76
774. Start the service:
78
79   ```sh
80   sudo systemctl enable --now ermite-runner
81   journalctl -u ermite-runner -f
82   ```
83
84`ermite-runner run` polls the server for jobs and runs each one in a sandbox, up to `ERMITE_RUNNER_CAPACITY` jobs at a time. On SIGTERM it cancels the running jobs, reports them and removes their sandboxes.
85
86To check an installation without a server, run `ermite-runner sandbox-check`. It runs one stub script in the selected mode.
87
88More on the service, logs and shutdown: [docs/service.md](docs/service.md).
89
90## Commands
91
92| Command | Purpose |
93| --- | --- |
94| `ermite-runner register [--token-stdin] [--token T] [--replace]` | Register with the server and store the runner token |
95| `ermite-runner run` | Fetch and run jobs |
96| `ermite-runner sandbox-check [script]` | Run one stub script in a sandbox |
97| `ermite-runner version` | Print the version |
98
99## Key settings
100
101| Variable | Default | Meaning |
102| --- | --- | --- |
103| `ERMITE_RUNNER_URL` | | Base URL of the ermite server. Must be `https://` |
104| `ERMITE_RUNNER_NAME` | host name | Runner name |
105| `ERMITE_RUNNER_LABELS` | | Extra labels, comma separated |
106| `ERMITE_RUNNER_ISOLATION` | `auto` | `auto`, `vm` or `container` |
107| `ERMITE_RUNNER_CAPACITY` | `1` | Jobs in parallel |
108| `ERMITE_RUNNER_JOB_CPUS` | `2` | CPUs per job |
109| `ERMITE_RUNNER_JOB_MEMORY` | `4G` | Memory per job |
110| `ERMITE_RUNNER_JOB_DISK` | `64G` | Scratch disk per job |
111| `ERMITE_RUNNER_STATE_DIR` | `/var/lib/ermite-runner` | State directory |
112
113All settings: [docs/configuration.md](docs/configuration.md).
114
115## Security notes
116
117- Run only one runner per host.
118- The registration token can read the code and secrets of every repository. Pass it through standard input.
119- Without sysbox, `container` mode uses a privileged container, which is equivalent to host root. Set `ERMITE_RUNNER_DENY_PRIVILEGED=1` to refuse it.
120- The state directory and all its parents must be owned by root and not writable by others.
121
122## Documentation
123
124- [Configuration](docs/configuration.md): every environment variable
125- [Sandboxes](docs/sandboxes.md): `vm` and `container` modes, network policy, cleanup
126- [Service](docs/service.md): systemd unit, logs, shutdown
127- [Images](docs/images.md): job image, guest kernel
128- [Workflows](docs/workflows.md): job and service containers, cancellation, annotations
129- [Cache and artifacts](docs/cache-and-artifacts.md): Actions cache and artifacts v4
130- [Node.js externals](docs/externals.md): Node.js for JavaScript actions
131- [Development](docs/development.md): build, tests
132
133## License
134
135MIT. See [LICENSE](LICENSE). Third-party attributions: [THIRD_PARTY.md](THIRD_PARTY.md).

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 11

↑↓ moveenter openesc close