builds.sr.ht runners: manifests, hosted limits and self-hosted build workers

On hosted SourceHut, builds.sr.ht runs each job in a virtual machine on SourceHut's own workers, and you need a paid account to submit builds. SourceHut offers no way to attach your own runner to the hosted service. If you need your own hardware, you run your own SourceHut instance with builds.sr.ht and one or more builds.sr.ht-worker hosts, which boot KVM images for each job.

How builds.sr.ht works

builds.sr.ht is SourceHut's CI service. You submit a YAML manifest (by pushing to git.sr.ht, sending a patch to a lists.sr.ht mailing list, through the web form, the API, or the hut CLI), and builds.sr.ht queues a job. A worker picks it up, boots a fresh virtual machine from the requested image, runs your tasks over SSH and destroys the VM.

The model differs from GitHub or GitLab runners. A SourceHut worker is part of the SourceHut installation itself: it reads jobs from a shared Redict (Redis-compatible) queue and writes results straight into the builds.sr.ht PostgreSQL database. Each job gets its own VM by design, so the "ephemeral runner" behaviour other platforms make you configure is the default here.

Build manifests: .build.yml

A minimal manifest needs an image and at least one task:

image: alpine/latest
packages:
  - go
sources:
  - https://git.sr.ht/~you/project
environment:
  CGO_ENABLED: "0"
tasks:
  - build: |
      cd project
      go build ./...
  - test: |
      cd project
      go test ./...
artifacts:
  - project/bin/app

The keys you will use most:

  • image: the OS image, such as alpine/latest, debian/stable, ubuntu/lts, archlinux, fedora/rawhide, freebsd/latest, openbsd/latest, nixos/latest or guix. The compatibility page lists all of them.
  • arch: the architecture. The compatibility page marks which combinations run on native hardware; the rest run emulated and are slow.
  • packages: packages installed with the image's package manager before the tasks run.
  • sources: repositories cloned into the build user's home directory.
  • tasks: named shell scripts, run in order.
  • environment, secrets (secret UUIDs), artifacts (files up to 1 GiB each, kept 90 days), triggers (for example email on failure) and shell (keep the VM alive for SSH).

git.sr.ht submits .build.yml from the repository root on each push, or up to four manifests from .builds/*.yml. With more than four, it picks four at random. Skip CI for a push with git push -o skip-ci, or point at other files with git push -o submit=".sourcehut/*.yml".

When a build fails, the VM stays up for ten more minutes and the build page shows an SSH command to log in and inspect it. Add shell: true to keep that access while the build runs.

Hosted sr.ht: paid accounts and no custom runners

Two limits apply to the hosted service.

You need a paid account to submit builds. SourceHut's pricing page lists builds.sr.ht as "Required" in the payment column, and the billing FAQ says you need to pay to create repositories, mailing lists, trackers or submit build jobs. Plans start at €4 or $4 per month, with tiers at 8 and 12. SourceHut offers financial aid to people who cannot afford a subscription. Contributors without a paid account can still send patches and take part in discussions.

You cannot attach your own worker to hosted builds.sr.ht. SourceHut's documentation describes no registration token, agent or API for bringing your own runner, and the worker design rules it out: a worker needs direct access to the instance's PostgreSQL database and its Redict queue, which SourceHut does not expose to users. Every hosted build runs on SourceHut's machines, with the images and architectures SourceHut provides, inside the build time limits it sets.

Your real options for custom hardware

If a hosted builds.sr.ht job cannot do what you need (a GPU, more memory, access to a private network, a licensed tool), you have three paths:

  1. Self-host SourceHut with builds.sr.ht. You run the whole stack and add workers on your own hardware. The rest of this guide covers it.
  2. Keep code on sr.ht, run heavy jobs elsewhere. Mirror the repository to a forge with self-hosted runners, such as Forgejo Actions or Woodpecker CI, and run the hardware-specific jobs there. A builds.sr.ht task with a deploy secret can push to the mirror.
  3. Call out from a hosted build. A builds.sr.ht task can use a secret to trigger a job on infrastructure you control (an API call or SSH command) and wait for the result. You keep sr.ht as the visible CI and do the heavy work on your hardware.

Self-host builds.sr.ht with your own worker

A self-hosted builds.sr.ht needs a working SourceHut installation, at minimum meta.sr.ht for accounts plus builds.sr.ht itself, and in most setups git.sr.ht. SourceHut calls itself alpha software with no stable releases. Alpine Linux is the only environment SourceHut supports, with packages on mirror.sr.ht; the installation guide also points to the community-maintained sr.ht-container-compose setup. Subscribe to the sr.ht-admins mailing list for upgrade and security notices.

The worker pieces come in two packages:

  • builds.sr.ht-worker: the worker daemon, with an OpenRC service of the same name.
  • builds.sr.ht-images: image definitions, the genimg scripts that build them and the control script the worker calls to start VMs.

Workers can run on the same server as the web service or on separate machines. Each worker's config.ini needs the [sr.ht] section, [webhooks] private-key, [meta.sr.ht] origin, [mail] for notifications, [objects] if you want artifacts, and from [builds.sr.ht] at least origin, connection-string and redis. Then the worker section:

[builds.sr.ht]
origin=https://builds.example.org
connection-string=postgresql://builds_worker@db.internal/builds.sr.ht
redis=redis://queue.internal:6379/0
# Let accounts without billing submit builds on your instance
allow-free=yes

[builds.sr.ht::worker]
name=worker-01.internal:8080
buildlogs=/var/log/sr.ht/builds
images=/var/lib/images
controlcmd=doas -u docker /var/lib/images/control
timeout=45m
bind-address=0.0.0.0:8080
trigger-from=builds@example.org

Points to get right:

  • Shared queue. The master and every worker share one Redict database. Expose it on a LAN or VPN only, and secure it.
  • Database grants. The worker's SQL user needs read/write on the job, artifact and task tables and read-only on the user and secrets tables.
  • timeout caps build duration, using Go duration syntax (45m, 2h).
  • allow-free on the master decides whether non-paying accounts may submit builds. On a private instance without billing, set it to yes.

Build images and KVM

Each build runs in a KVM virtual machine, launched from a Docker container that holds only the QEMU components and runs as a non-root user. A worker host therefore needs hardware virtualization (/dev/kvm) and Docker. On a cloud VM, that means an instance type with nested virtualization or a bare-metal host.

Build the QEMU container on each worker:

cd /var/lib/images
docker build -t qemu -f qemu/Dockerfile .

Images live under /var/lib/images/$distro/$release/$arch/root.img.qcow2. You bootstrap them with the genimg script for each distribution, run from a working system of that same guest, and rebuild them on a schedule so packages stay current. Only the images you build exist on your instance, so a manifest asking for openbsd/latest fails until you add that image.

Security model

builds.sr.ht runs arbitrary user code as root inside the guest by design. SourceHut's guidance:

  • Put workers on dedicated servers, isolated from production systems and data.
  • Keep the build user out of the docker group. Create a separate user that is, and allow the build user to run only the control script through doas or sudo, as in the controlcmd example above (permit nopass builds as docker cmd /var/lib/images/control).
  • Treat any secret that appears in a public or unlisted build log as compromised. builds.sr.ht disables secrets for builds submitted through mailing lists, so patches from strangers cannot read them.

Troubleshooting

Hosted build submission rejected for billing reasons

The account that owns the job has no active subscription. Subscribe, request financial aid, or submit from the paid account.

No builds start on a push

Check the file name (.build.yml, or .builds/*.yml), YAML syntax and the push options; skip-ci set in push.pushOption suppresses every submission.

Self-hosted jobs stay queued

The worker cannot reach Redict, or its redis URL points at a different database number than the master's. Compare both config.ini files and the worker logs.

VM fails to boot on a self-hosted worker

The host lacks /dev/kvm, the qemu Docker image is missing, or the requested image path does not exist under images. Run the control script by hand with the same user to see the error.

Cost trade-offs

On hosted sr.ht, CI comes bundled with the subscription and you run nothing. Self-hosting SourceHut to get your own workers means operating PostgreSQL, Redict, several Python and Go services, mail, and KVM-capable hosts on an alpha codebase. That pays off for organizations that already want a self-hosted forge or need builds on hardware SourceHut does not offer. For a team that only needs a GPU job now and then, a mirror to a forge with self-hosted runners costs less effort.

FAQ

Can I use my own runner with builds.sr.ht?

Not on the hosted sr.ht service. You can run your own workers only on a SourceHut instance you host yourself.

Is builds.sr.ht free?

No. Submitting builds on hosted sr.ht requires a paid account, starting at €4 or $4 per month, and SourceHut offers financial aid. On a self-hosted instance, allow-free=yes lets accounts without billing submit builds.

Does builds.sr.ht support arm64?

Several images list aarch64 or arm64. The compatibility page marks which run on native hardware and which run emulated.

Can builds.sr.ht run macOS or Windows jobs?

The image list covers Linux distributions and the BSDs, with no macOS or Windows images. Use another CI, such as GitHub Actions with self-hosted runners, for those platforms.

SourceHut and cirunner.dev

cirunner.dev provisions a fresh VM per job, registers it with your CI using a token you provide, scales with the queue and destroys the machine after the job. You choose CPU, RAM, x86_64 or arm64, region, base image and an optional GPU, and pay per compute minute.

The service is in early access with GitHub Actions and GitLab CI as launch platforms. SourceHut builds is on the roadmap, though hosted sr.ht offers no runner registration today; vote for it on the early-access form if you run your own SourceHut instance and want managed workers.

Skip the runner fleet

cirunner.dev boots a fresh VM for every SourceHut builds job, registers it, and destroys it when the job ends. SourceHut builds is on our roadmap. Vote for it on the early-access form.

Join early access