# Choosing an `image:` Pick the smallest, fastest image that has the packages you need. This page is a quick selector. ## Default recommendation **`alpine/edge`** unless you have a specific reason otherwise. Boot is fast (~5 seconds), `apk` install is fast, packages are recent. Most projects work on Alpine. `alpine/latest` is also fine if you want the latest stable rather than edge (rolling). ## When to switch off Alpine - **glibc-required binaries** (you have a pre-built binary that depends on glibc). Alpine uses musl. Switch to `debian/stable` or `ubuntu/lts`. - **Distro-specific packaging** — building a `.deb` → `debian/stable`; building a `.rpm` → `fedora/latest`; AUR things → `archlinux`. - **The package you need is in this distro and not Alpine** — happens occasionally. Search the relevant distro's package index. - **Reproducing a customer's environment** — match their distro. ## Self-hosted instances (`*.srht.bigb.es` and similar) Upstream `builds.sr.ht.org` ships a large pre-built image catalog. A self-hosted instance ships only: 1. **Recipes** — shell scripts under `/var/lib/images///` installed by the `builds.sr.ht-images` Alpine package. These describe how to build a qcow2 but are not themselves bootable. 2. **Whatever the operator has actually built** with `genimg`. Each recipe must be run once (and re-run when you want a fresh base) to produce `/var/lib/images////root.img.qcow2`. Submitting `image: /` against a self-hosted instance fails with "no such image" until the qcow2 has been built. Before recommending an image for someone's self-hosted instance, check whether they've built it. The skill's surrounding `phoebe-lab/srht` repo exposes `just list-built-images` for this. Self-hosted instances also commonly ship a **subset** of the upstream catalog. The `builds.sr.ht-images` apk package (the source of recipes) does not include `9front` or `gentoo`; instances that want those have to add them manually. Don't assume an image exists on a self-hosted instance just because it works on `builds.sr.ht.org`. ## Per-distro notes ### Alpine (`alpine/*`) - Package manager: `apk`. Install: `apk add ` (the manifest's `packages:` does this). - musl libc, BusyBox utils. Some scripts assume GNU coreutils — install `coreutils` if a script uses `--features` not in BusyBox. - Aliases (as of upstream compatibility matrix, 2026): - `alpine/edge` — rolling, daily refresh. - `alpine/latest` = `alpine/3.23` (current stable). - `alpine/old` = `alpine/3.22`, `alpine/older` = `alpine/3.21`, `alpine/oldest` = `alpine/3.20`. - `alpine/edge` boots in ~5 seconds. Tiny image. - `python3` requires `py3-pip` separately. Go is `go`. Node is `nodejs` + `npm`. Rust is `rust` + `cargo`. - Custom apk repositories use the three-word form `repo-url key-url key-name` in `repositories:`. ### Arch (`archlinux`) - Package manager: `pacman`. Rolling release. - AUR not available out of the box; install `yay` or `paru` via packages, then build. - Often the cleanest for "I just need a recent version of " because rolling. - Hugo is the extended (Sass-capable) build here, unlike Alpine. ### Debian (`debian/`) - Package manager: `apt`. Older but stable packages. - `apt-get install` needs `-y` to skip prompts; the manifest's `packages:` handles this. - Build essentials are in `build-essential`. - Python is `python3` + `python3-pip`. Node is `nodejs` + `npm` (modern Debian). - Aliases (2026): - `debian/stable` = `debian/trixie` (Debian 13). - `debian/oldstable` = `debian/bookworm` (Debian 12). - `debian/testing` = `debian/forky`. - `debian/unstable` = `debian/sid`. - Both `debian/stable` / `debian/testing` and the codenames are valid; pick whichever you'd rather not have to update in two years. - Default arch is `amd64`, not `x86_64`. ### Ubuntu (`ubuntu/`) - Like Debian, but Canonical's spin. `apt` for packages. - Aliases (2026): - `ubuntu/lts` = `ubuntu/resolute` (26.04 LTS). - `ubuntu/oldlts` = `ubuntu/noble` (24.04 LTS). - Other shipped: `focal` (20.04 LTS), `jammy` (22.04 LTS), `oracular` (24.10), `plucky` (25.04), `questing` (25.10). - Default arch is `amd64`, not `x86_64`. Slower boot than Alpine. Larger image. ### Fedora (`fedora/`) - Package manager: `dnf`. Recent packages. - Aliases (2026): - `fedora/latest` = `fedora/43`. - `fedora/rawhide` = `fedora/44` (pre-release). - Older shipped: `fedora/42`. - The `repositories:` invocation differs between Fedora 41+ (`dnf config-manager addrepo`) and Fedora 40- (`dnf config-manager --add-repo`); the worker handles it but the `.repo` file format varies. - Common for RPM packaging and Red Hat-adjacent workflows. ### Rocky Linux (`rockylinux/`) - Package manager: `dnf`. RHEL-compatible (replaces CentOS for RHEL-clone testing). - Versions shipped: `rockylinux/8`, `rockylinux/9`. - Use when you need to validate against RHEL-like userspace without RHEL licensing. ### FreeBSD (`freebsd/`) - Package manager: `pkg`. BSD make and BSD coreutils. - Aliases (2026): - `freebsd/latest` = `freebsd/15.x`. - `freebsd/current` = `freebsd/16.0-CURRENT` (pre-release). - Also shipped: `freebsd/14.x` (14.4-RELEASE). - Default arch is `amd64`, not `x86_64`. Custom package repositories are **not supported** — only the standard `pkg` repos work. - Useful for cross-platform testing or BSD-targeted builds. - Some GNU tools have different flag syntax; check before relying on them. ### OpenBSD (`openbsd/`) - Package manager: `pkg_add`. Base system gets `syspatch` for binary patches. - Aliases (2026): `openbsd/latest` = `openbsd/7.8`, `openbsd/old` = `openbsd/7.7`. - Custom package repositories are not supported. - Similar use case to FreeBSD; smaller, tighter system. ### NetBSD (`netbsd/`) - pkgsrc via `pkgin`. - Aliases (2026): `netbsd/latest` = `netbsd/10.x` (10.0). Also: `netbsd/9.x` (9.3). - Custom package repositories are not supported. - Useful for portability testing across all the BSDs. ### NixOS (`nixos/`) - Nix works out of the box; **flakes are not enabled by default** — set `NIX_CONFIG: "experimental-features = nix-command flakes"` in `environment:` to opt in. - `packages:` uses `nix-env -iA`, so you need a selection path: `nixos.hello`, not bare `hello`. - Channels shipped (2026): `nixos/unstable`, `nixos/latest` = `nixos/25.05`, also `nixos/24.11`. - Custom channels via `repositories:` (`channel-name: channel-url`) run `nix-channel --add` + `--update` for you. The pre-installed root channel is `nixos`, pointing at the image's release (or `nixos-unstable` for `nixos/unstable`). - Best image for reproducible builds; once flakes are on, `nix build .#mypackage` is the whole task. ### Guix (`guix`) - Similar to NixOS but with Guile-based config. Niche but supported. - No version segment — single rolling recipe. ### Gentoo and 9front The upstream sr.ht-docs compatibility matrix (2026) lists these distros for builds.sr.ht: Alpine, Arch, Debian, Fedora, FreeBSD, Guix, NetBSD, NixOS, OpenBSD, Rocky Linux, Ubuntu. It does **not** list Gentoo or 9front. The `builds.sr.ht` repo's `images/` directory contains recipes for the distros above plus `qemu` and a `control` script — no `gentoo/` or `9front/` directories. Submitting `image: gentoo` or `image: 9front` will fail on every standard sr.ht instance (upstream included) with "no such image" until an operator adds and builds those recipes by hand. Do not generate manifests targeting them by default. ## Architecture support Default is `x86_64`. Other architectures are available on a subset of images: - `aarch64` — available on `alpine/*`, recent `debian/*`, recent `freebsd/*`, `archlinux` (varies). - `riscv64` — `alpine/edge` typically. - Others (`armv7`, `ppc64le`) — case-by-case, check the compatibility page. To use: ```yaml image: alpine/edge arch: aarch64 ``` Not every image+arch combination exists. If submission fails with "no such image", the combination isn't supported. ### How arch routing actually works On the worker, qcow2 disks live at `/var/lib/images////root.img.qcow2`. The `/var/lib/images/control` script reads `arch:` from the job, checks that path, and: - If host arch matches guest arch and `/dev/kvm` exists → KVM acceleration (~native speed). - If they differ → QEMU TCG software emulation (~5–20× slower). Recipes (`//functions`) gate which archs they support. Most upstream recipes default to `x86_64` and explicitly reject other arches — e.g. Alpine's `functions` has `default_arch=x86_64` and a hard error otherwise. So "this distro supports aarch64" depends on the recipe, not just the worker's QEMU binaries. For self-hosted instances: adding a new arch means (a) installing `qemu-system-` in the worker, (b) patching each recipe's `functions` to drop the arch guard and fetch arch-specific bootstrap, and (c) rebuilding the qcow2 under the new arch directory. Performance on a mismatched host (e.g. aarch64 on x86_64 hardware) makes this mostly useful for "does it compile?" CI, not real workloads. For real cross-arch CI, the right pattern is a **separate worker on native hardware** — builds.sr.ht is multi-worker and the scheduler routes jobs by the `arch:` field to the worker that has a matching built image. ## Trade-off summary | Image | Boot speed | Image size | Package recency | Best for | |---|---|---|---|---| | `alpine/edge` | ★★★★★ | Tiny | Recent | Default | | `archlinux` | ★★★★ | Medium | Bleeding-edge | Rolling deps, AUR | | `debian/stable` | ★★★ | Medium | Stable, older | Debian packaging, glibc | | `ubuntu/lts` | ★★ | Larger | Stable | Canonical workflows | | `fedora/latest` | ★★★ | Medium | Recent | RPM packaging | | `freebsd/latest` | ★★ | Medium | Recent | BSD testing | | `nixos/unstable` | ★★ | Larger | Rolling | Reproducible builds | ## Verifying package availability Before committing a manifest, verify the package name in the target image's repos: - Alpine: - Arch: - Debian: - Ubuntu: - Fedora: - FreeBSD: Package names vary: `ninja` (Alpine, Arch, Fedora) vs `ninja-build` (Debian, Ubuntu). When in doubt, look it up — the failure mode of guessing is a wasted CI run, but the lookup takes 5 seconds.