# External runners

> Show your own self-hosted GitLab or GitHub runner servers in the Manage Runners dashboard, with CPU, RAM, swap and disk metrics and OpenTelemetry export, by installing the metrics agent with one command.

Source: https://managerunners.com/docs/manual/external-runners/

An external runner is a GitLab or GitHub runner server that you run yourself, for example a dedicated Docker host in your office or a bare-metal server elsewhere. Manage Runners does not create, change or pay for it. You install the same metrics agent that runs on managed runners, and the server appears in the dashboard next to your other runners, with the same [Metrics](https://managerunners.com/docs/manual/metrics/): charts in the dashboard, export to your own OpenTelemetry backend, or both.

External runners are available on every plan. Like Metrics on managed runners, adding and changing them and creating install commands need an active plan for the organization; see [Billing and plans](https://managerunners.com/docs/manual/billing/).

What external runners do not have: pause and resume, schedules, duplicate, SSH keys, price, location and server type. Manage Runners never registers, restarts or reconfigures the runner software on the server.

## Requirements

The agent is a small Python program that runs as a systemd service. The server needs:

- Linux with systemd running as the init system,
- Python 3.8 or later at `/usr/bin/python3`,
- outgoing HTTPS to `api.managerunners.com` and, if you use **Send to**, to your OpenTelemetry endpoint,
- root access to run the install command, and `curl` to download it.

Debian 13 and Ubuntu 24.04 are tested. Other current distributions with systemd, such as Fedora, Rocky Linux or openSUSE, are likely to work but are not tested. The installer installs no packages; if Python is missing, install it with your package manager first, for example `apt-get install python3`.

## Add an external runner

1. In the dashboard, select **Add external runner** above the runner list.
2. Enter a **Name** (letters, digits, spaces and `-`), choose the **Platform** (GitLab or GitHub; it is for your overview only) and add **Labels** if you like, for example the runner's tags.
3. Under **Metrics**, choose **Show in dashboard**, a **Send to** connection, or both, as for a managed runner.
4. Select **Add runner**. The install dialog opens.

The runner shows **Waiting for install** until the agent on the server has sent its first report.

## Install the agent

The install dialog shows a command like this:

```sh
(umask 077 && rm -f mr-install.sh && curl -fsSL -o mr-install.sh 'https://api.managerunners.com/public/install/…') && sudo sh mr-install.sh
```

Run it in a directory that no other user or CI job can write to, such as your home directory; not in `/tmp`, a shared folder or a CI workspace. The downloaded `mr-install.sh` contains the server's credential. In such a directory the command removes an older file of that name, creates the new one readable only by you, and starts the installation only after the download has completed. In a directory others can write to, they could replace the file between the download and `sudo`, or read it. The `umask` applies only inside the parentheses, not to your shell. Don't open the link in a browser or link checker: the first download uses it up.

Copy it with **Copy** and run it on the server. The link in it works **once** and for **15 minutes**. If it has expired or was used, select **New install command**. You can also open the dialog later with **Install command** on the runner's card.

The script:

1. checks that it runs as root on Linux with systemd and Python 3.8 or later, and stops with a clear message otherwise,
2. creates the system user `managerunners-monitor`,
3. writes the agent to `/usr/local/libexec/managerunners-monitor.py`, its configuration with the server's credential to `/etc/managerunners-monitor.conf` (readable only by root and the agent) and the systemd unit `managerunners-monitor.service`, the same files and hardening as on managed runners,
4. enables and starts the service, and waits until Manage Runners has accepted the agent's first report. This usually takes a few seconds; the script shows the time elapsed and gives up after 90 seconds.

Running it again overwrites the files and restarts the service, so it is also how you update and reinstall the agent. Agents installed before version 1.1.0 show **Reinstall needed** until you run a new install command. Delete `mr-install.sh` after the installation, or keep it only to uninstall later.

With the CLI (1.6.0 or later): `manage-runners external-runner install-command --name <name>`. The MCP server cannot create install commands, so the credential they contain never reaches an AI assistant's conversation.

## Running jobs on GitLab runners

For GitLab runners, the agent also reports how many CI jobs are running. It reads them from GitLab Runner's metrics endpoint on the server itself, `127.0.0.1:9252`. If the installer cannot reach it, it prints a hint. To report the jobs, add this line to the global section at the top of `/etc/gitlab-runner/config.toml`, then restart GitLab Runner with `sudo systemctl restart gitlab-runner`:

```toml
listen_address = "127.0.0.1:9252"
```

The installer never changes your runner's configuration. Keep the address on `127.0.0.1`, so the endpoint is not reachable from other machines. GitHub runners have no such endpoint and report no job count.

## Status

| Status | Meaning |
| --- | --- |
| **Waiting for install** | No report has arrived since the last install command was used, or none was used yet. |
| **Online** | The agent reported within the last three minutes. It reports about once a minute. |
| **Offline** | The last report is older than three minutes: the server, the service or its network is down. Check `systemctl status managerunners-monitor` on the server. |
| **Not reporting** | The server's credential has ended, so its agent stopped: **Show in dashboard** was switched off (this always ends the credential, even with a **Send to** connection), the runner's connection was deleted, or the plan is no longer active. With a destination selected and an active plan, run a new install command; with neither destination selected, uninstall the agent. |

The card shows the server's CPU, RAM and disk size from the agent's latest report. **CPU** shows threads and physical cores, for example **8 cores / 16 threads**, when the CPU runs two threads per core (SMT or Hyper-Threading), and **N CPUs** when both are the same or the cores cannot be read. CPU usage in percent always refers to all threads. Hetzner's vCPUs on managed runners are threads in the same sense.

## Change settings and reinstall

**Edit** on the card changes the name, platform, labels, **Show in dashboard** and **Send to**. Unlike a managed runner, nothing on the server changes when you save: the agent keeps its installed settings until you run a new install command. The card shows **Reinstall needed** with the reason when:

- **Show in dashboard** or **Send to** changed, the connection's endpoint or headers were edited, the connection was deleted, or the runner was renamed while it sends to a connection,
- a newer agent is available,
- the server's credential has ended.

Switching **Show in dashboard** off always ends the server's credential, so its agent stops, including any export to a connection, until you reinstall it.

Editing a connection's endpoint or headers never touches external runners: they keep sending with the previous settings and show **Reinstall needed**. Deleting a connection ends the credential of the external runners that use it, including those whose agent was installed with it before you changed **Send to** without reinstalling, so their agents stop sending, also to the dashboard. The **Metrics** page lists them before you delete. A runner that still has **Show in dashboard** on shows **Reinstall needed**; one that is left with neither destination shows **Not reporting** and suggests uninstalling the agent, because a new install command would have nothing to install.

## Delete an external runner and uninstall the agent

**Delete** on the card removes the runner from Manage Runners and ends the server's credential, so the agent stops at its next report. Its metrics history is no longer shown. The server and its runner keep running.

To remove the agent from the server, run this in the folder of the install script:

```sh
sudo sh mr-install.sh --uninstall
```

It stops and disables the service and removes the unit, the agent, its configuration and the `managerunners-monitor` user. If you no longer have the script, run the same steps by hand:

```sh
sudo systemctl disable --now managerunners-monitor.service
sudo rm -f /etc/systemd/system/managerunners-monitor.service /usr/local/libexec/managerunners-monitor.py /etc/managerunners-monitor.conf
sudo rm -rf /run/managerunners-monitor
sudo systemctl daemon-reload
sudo userdel managerunners-monitor
sudo groupdel managerunners-monitor 2>/dev/null || true
```

## With the CLI and AI assistants

The CLI commands need CLI 1.6.0 or later.

- `manage-runners external-runner list`, `get`, `create`, `update`, `delete` and `install-command` manage external runners; `manage-runners runner list` shows them under `externalRunners`.
- [`manage-runners runner metrics`](https://managerunners.com/docs/cli/reference/runner-metrics/) reads their metrics like those of managed runners.
- Through the [MCP server](https://managerunners.com/docs/manual/connect-to-claude/), `list_external_runners`, `get_external_runner` and `get_runner_metrics` read them. AI assistants cannot add, change or delete them or create install commands.
