Drivers

The driver creates and removes one ephemeral runner per job. Set it in the driver block of agent.yaml.

tartmacOS VMs on Apple Silicon. The default when type is empty.
lumemacOS VMs on Apple Silicon, through the lume CLI.
dockerLinux containers through Docker or Podman.

Keys for every driver

typetart, lume or docker.
imageThe base VM or container image for each runner.
capacityHow many runners this host runs at the same time.
min_free_gbFree-space floor for the image store. Below it, the host stops taking jobs and reclaims space. 0 uses the driver default. A negative value turns off the gate but keeps reclaim.

Tart

driver:
  type: tart
  image: paddo-runner-mac   # base VM, cloned for each runner
  capacity: 2               # Apple's limit for macOS guest VMs
  # min_free_gb: 60         # measured on ~/.tart

Defaults: image paddo-runner-mac, capacity 2, min_free_gb 60. If the image is missing, ushr login offers to clone ghcr.io/cirruslabs/macos-runner:tahoe under that name. The agent installs the Actions runner into each clone. See also Tart and Lume.

Lume

driver:
  type: lume
  image: paddo-runner-mac   # a local lume VM, cloned for each runner
  capacity: 2

Install lume and put it on your PATH first. Neither ushr login nor ushr doctor installs it. Create the base VM in lume yourself. Defaults match Tart: capacity 2, min_free_gb 60.

SSH inside macOS VMs

SSH is not a separate driver type. Tart and Lume both use it. After a clone boots, the agent connects to the VM over SSH as the user admin. It waits up to 60 seconds for SSH. Then it installs the Actions runner and starts it with the job's JIT config. The agent checks over SSH when the runner process exits.

Your base image must accept SSH as admin from the host. The Cirrus Labs runner image that ushr login clones already does.

Docker and Podman

driver:
  type: docker
  # runtime: podman        # empty = docker, then podman
  image: ghcr.io/actions/actions-runner:latest
  capacity: 4              # size to host CPU and RAM
  # min_free_gb: 25
  # memory: "8g"           # per-runner limit
  # network: host
  # exclusive_daemon: true
  # build_cache: false     # per-repo build caches are on by default
runtimedocker or podman. Empty detects docker first, then podman.
capacityDefault 4.
min_free_gbMeasured on the container data root. Default 25.
memoryMemory limit for each runner container, for example "4g". A heavy build then stops its own job, not the host.
networkContainer network, for example host.
volumesExtra mounts. Mount the host docker socket here for buildx.
group_addExtra groups. Add the host docker group ID so the runner user can use the socket.
extra_hostsExtra host-to-IP entries for the container.
entrypointRunner entrypoint. Default /home/runner/run.sh. It must accept --jitconfig.
exclusive_daemonSet true only when ushr runners are the only users of this daemon. Reclaim then also removes containers, volumes and images that jobs leave. Default false.
build_cacheGives each repo its own persistent buildx builder. Default on. Needs the buildx plugin; without it, jobs run uncached.
buildkit_imageDefault moby/buildkit:buildx-stable-1.
build_cache_gbCache limit per repo before a prune. Default 20.
build_cache_idle_hoursRemove a repo builder after this many idle hours. Default 168.
state_dirWhere per-repo buildx configs live. Default ~/.local/share/ushr.
The build cache separates caches between repos. It is not a security boundary, because jobs share the host docker socket. Do not run untrusted pull requests on self-hosted runners.

next Tart and Lume →