Self-host a single host
Run the controller and the agent on one machine. You need no ushr.io account. The controller and the agent send no data to ushr.io.
1. Write the controller config
The controller service reads ~/.config/ushr/controller.yaml. Create it before you run setup.
version: "1"
listen: 127.0.0.1:7080
token: "" # empty = no auth; the controller then binds loopback only
policy:
type: priority
aging:
boost_per_minute: 1With an empty token, the controller refuses a non-loopback listen address. Set a token in both files if agents connect from other machines.
2. Create the GitHub App
ushr setup --org YOUR_ORG- It writes a default
~/.config/ushr/agent.yamlif none exists. That config points the agent athttp://127.0.0.1:7080. - It opens a local page that sends an App manifest to GitHub. You confirm the App on GitHub.
- It writes the App private key to
~/.secretsand opens the App install page. - It adds the org to the
orgslist inagent.yaml. - It installs and starts the
ushr-controllerandushr-agentservices. The controller service starts only whencontroller_urlis a loopback address.
Personal accounts have no org-level runners. Use --repo OWNER/REPO instead of --org.
Flags
| --org | GitHub organisation to create the App in (org-level runners). |
| --repo | owner/repo to create the App for (repo-level runners). Use exactly one of --org or --repo. |
| --priority | Scheduling priority for this target. Higher wins. Default 100. |
| --key-dir | Directory for the App private key. Default ~/.secrets. |
| --config | Agent config to record the App in. Default ~/.config/ushr/agent.yaml. |
| -y | Answer yes to prompts. If the scope already has an App, -y keeps it. |
If the scope already has an App, setup asks before it creates a second one. Every run of the manifest flow registers a real GitHub App.
The agent config
After setup, a macOS agent.yaml looks like this:
version: "1"
controller_url: http://127.0.0.1:7080
token: ""
name: mac-runner-1
labels:
- self-hosted
- macOS
- ARM64
driver:
type: tart
image: paddo-runner-mac
capacity: 2
orgs:
- name: YOUR_ORG
app_id: 123456
private_key_path: /Users/you/.secrets/ushr-1a2b3c-YOUR_ORG.pem
priority: 100
source:
type: poll
interval: 30s| controller_url | Where the agent polls for work. |
| token | Bearer token. Must match token in controller.yaml. |
| name | Agent name. Defaults to the host name. |
| labels | Labels this host serves. Jobs match on runs-on. |
| orgs[].repos | Optional. Poll only these repo names. Use it on large installations to stay under the GitHub API rate limit. |
| orgs[].runner_group_id | Optional. GitHub runner group for JIT runners. Default 1 (the Default group). |
| repos[] | Per-repo targets: owner, repo, app_id, private_key_path, priority. |
| source.type | poll or scaleset. |
| source.interval | How often the poll source checks GitHub. Example 30s. |
Runner scale sets
With source.type: scaleset, GitHub sends desired runner counts and the agent does not poll. Declare the sets under each org. The set name is also the runs-on label.
orgs:
- name: YOUR_ORG
app_id: 123456
private_key_path: /Users/you/.secrets/ushr-1a2b3c-YOUR_ORG.pem
scale_sets:
- name: my-scale-set
max_runners: 8
source:
type: scalesetSet max_runners to the capacity of the agents that serve the set.