A registry on the fleet
examples/registry/ runs a real Podman registry on a golem-managed box, and uses
it from another. It is the first half of golem’s dogfood loop: one VM hosts a
registry, another pushes to it — no shared L2 segment, just golem and QEMU’s
host gateway.
Start from Bring up the fleet; this needs the same harness and the same ssh-plus-token posture. Commands are nushell, from the repo root.
The registry, as one Workload
registry.emet is a single Quadlet.Workload — the
docker.io registry:2 image on :5000, backed by a named volume, internally
exposed:
import Quadlet exposing ( image, tcp, Restart(..), Access(..), Relabel(..) , fromVolume, Expose(..), Workload(..), workloadGlyphs )
registry : Workloadregistry = Workload { name = "registry" , image = image "docker.io/library" "registry" "2" , env = [] , ports = [ tcp 5000 5000 ] , volumes = [ fromVolume "golem-registry-data" "/var/lib/registry" ReadWrite Private ] , restart = Always , expose = Internal }
main : List Scrollmain = [ scroll { name = "registry" , notifies = [ "golem-nftables.service" ] , glyphs = workloadGlyphs registry } ]It lowers to eleven glyphs — the podman package, the
golem-registry-data.volume unit, the .container quadlet, its service, the
nftables base, and the drop-in that opens :5000 to the internal network:
main : List Scrollplanned scrolls (1): scroll `kaiju` (11 glyphs): * ensure apt package `podman` installed * ensure file `/etc/containers/systemd/golem-registry-data.volume` (mode 0644) * ensure file `/etc/containers/systemd/registry.container` (mode 0600) * enable + start systemd unit `registry.service` * ensure apt package `nftables` installed * ensure directory `/etc/nftables.d` (mode 0755) * ensure file `/etc/golem-nftables.conf` (mode 0755) * ensure file `/etc/nftables.d/00-base.nft` (mode 0644) * ensure file `/etc/systemd/system/golem-nftables.service` (mode 0644) * enable + start systemd unit `golem-nftables.service` * ensure file `/etc/nftables.d/registry-5000.nft` (mode 0644) ↻ kaiju notifies golem-nftables.serviceBoot the three boxes
The registry is only useful with something to push to it and something to serve
from it, so bring up all three now. --publish adds a host-to-guest forward,
which is unrelated to golemd — golemd is still loopback-only on every guest.
-
Boot and deploy. The
webbox publishes on 8081 only because 8080 was busy here; any free port works.Terminal window fleet up --hosts kaiju,talos,remora --publish kaiju=5000:5000 --publish remora=8081:80fleet deploy --hosts kaiju,talos,remora -
Apply the registry and builder scrolls. On cold guests this is the long step —
apt update, installing podman, pullingregistry:2. Many minutes, streaming progress the whole time; a quiet terminal is work, not a hang.Terminal window fleet apply examples/registry/registry.emet --hosts kaijufleet apply examples/website/builder.emet --hosts talosThe builder scroll is two glyphs: podman, and a
registries.conf.dfragment marking10.0.2.2:5000insecure so podman will push to it over plain HTTP. -
Confirm the registry is serving. Its
:5000is published to your workstation:Terminal window curl -s http://127.0.0.1:5000/v2/fleet ssh kaiju -- systemctl status registry.serviceAn empty registry answers
/v2/with an empty JSON object, andsystemctlreportsregistry.serviceasactive (running):{}
When it does not come up
curl: (7) Failed to connect to 127.0.0.1:5000
The guest was booted without --publish kaiju=5000:5000. Forwards are
recorded per VM at boot, so re-run fleet up --hosts kaiju --publish registry=5000:5000 — a resume can add forwards the stopped guest lacked.
The apply reports the registry leaf failed
Read the pull error in the forensics block under it, then fleet ssh kaiju -- journalctl -u registry.service -n 50 for what podman said. No sudo
needed: cloud-init put golem in systemd-journal.
registry.service is active (running) but the catalog curl hangs
The container is up and the ruleset that opens :5000 is not.
fleet ssh kaiju -- systemctl status golem-nftables.service says whether
the reload the scroll’s notifies asked for actually ran.
How the builder reaches the registry
Each guest runs behind user-mode (SLIRP) networking and is isolated from the others — only ssh is forwarded in by default. Two facts bridge them:
up --publish kaiju=5000:5000binds your127.0.0.1:5000to the registry guest’s:5000.- In SLIRP, every guest reaches your workstation at
10.0.2.2.
So any guest reaches the registry’s published port at 10.0.2.2:5000 — the
connection lands on your loopback, which QEMU forwards into the registry guest.
That rendezvous is the whole reason the builder scroll names 10.0.2.2:5000:
registryEndpoint : StringregistryEndpoint = "10.0.2.2:5000"
builderGlyphs : List GlyphbuilderGlyphs = [ aptPackage { name = "podman" } , file { path = "/etc/containers/registries.conf.d/golem-registry.conf" , contents = insecureConfContents -- marks 10.0.2.2:5000 insecure , mode = "0644" } ]The mechanics are in The fleet harness.
Push something to it
fleet ssh talos -- "sudo podman pull docker.io/library/hello-world:latest && sudo podman tag docker.io/library/hello-world:latest 10.0.2.2:5000/hello:latest && sudo podman push 10.0.2.2:5000/hello:latest"curl -s http://127.0.0.1:5000/v2/_catalog{"repositories":["hello"]}The quoted && chain runs in the guest’s bash over ssh, so nushell never sees
it.
One golem-hosted registry, reachable from a machine that shares no network segment with it.
Explore from here
- Watch the diff. Re-apply
registry.emetafter bumping the image tag — only the.containerglyph’s content id changes, so only that glyph is replaced. - Reverse it. Apply a
registryscroll with an emptyglyphslist and watch golem stop the service, remove the quadlet and.volumeunits, close the firewall port, and remove podman — each undone because golem recorded it. - Inspect the volume.
fleet ssh kaiju -- sudo ls /var/lib/registry— the named volume that survives a container restart.
Where to next
- The website loop — build the docs into an
image, push it to this registry, and serve it from the
webbox. - The Quadlet library — every field of the
Workloadthis registry is.