Sealed Images
A sealed release boots only what your org signed, and runs only the files it was built with. It carries a Unified Kernel Image signed with your org's Secure Boot key, and the UKI's kernel command line pins the composefs digest of the release's entire filesystem. With Secure Boot enforced, the chain is:
- Firmware checks your signature on systemd-boot.
- systemd-boot checks your signature on the UKI.
- The initramfs mounts only the filesystem whose digest the UKI names.
- The kernel checks every file it reads against that digest with fs-verity.
Change one byte of /usr and the file does not read. Swap the UKI and the
firmware does not boot it. This is bootc's
sealed image
chain; Kheeper makes the keys, does the signing on your machine and gets the
keys into the firmware.
Sealed images are new and bootc's composefs backend, which they run on, is still marked experimental. Today they work on bare metal installed over iPXE, and Kheeper has tested them on latitude.sh.
What you need
- A Linux machine with podman to release from. Sealing runs a container there, next to your signing key.
- A host installed with the composefs backend (step 2). An existing host can't be converted: bootc can't move a host between backends.
- Images built
FROM us.kheeper.com/public/composefs-baseinstead ofpublic/base. - Access to the host's firmware setup to turn Secure Boot on, once.
Step 1 — Create your org's keys
kheeper secureboot init myorg
This makes the three Secure Boot keys — the platform key (PK), the key
exchange key (KEK) and the signature database key (db) — on your machine, in
~/.config/kheeper/secureboot/myorg/, and registers their certificates with
your org. The private keys never leave your machine. Only an org owner can
register keys.
db.keysigns every sealed release. Whoever releases needs it.PK.keyandKEK.keyare needed only to change the keys. Keep them offline.
Check what's registered with kheeper secureboot show myorg.
Step 2 — Install a composefs host
Create the iPXE script with --composefs and boot it the
usual way:
kheeper hosts ipxe create myorg/vault --composefs
The Fedora 45 installer puts the host on an ext4 root (composefs needs fs-verity, which xfs lacks), and it boots and registers itself. It isn't sealed yet: it boots an unsigned UKI, and Secure Boot is off.
Step 3 — Build on composefs-base
FROM us.kheeper.com/public/composefs-base:v0.1.0
RUN dnf install -y nginx && systemctl enable nginx
composefs-base is base with systemd-boot instead of grub and shim. It takes the same configuration, and you push it the same way.
A sealed release's kernel command line is fixed when it's sealed. Put kernel
arguments in /usr/lib/bootc/kargs.d/ in your image.
Step 4 — Release it sealed
kheeper releases create myorg/vault:v1 \
--image us.kheeper.com/myorg/app:v1 \
--config-file config.json \
--seal --activate
--seal puts together the release exactly as the host will run it, computes
its composefs digest, and signs a UKI that commits to it with your db key. The
first sealed release downloads your image once into ~/.cache/kheeper/seal;
after that only the release's own layers move.
The release names your image by digest: the seal covers the exact image, so a tag that moved later must not change what the host gets.
Step 5 — The host enrols your keys
On its first sealed boot the host puts your signed systemd-boot on its EFI
system partition and, if its firmware is in Setup Mode — no platform key
enrolled, which is how bare metal on latitude.sh arrives — writes your db, KEK
and PK into the firmware. kheeper hosts get myorg/vault then shows:
SecureBoot off
off means your keys are enrolled but the firmware isn't enforcing them yet.
setup means the host never left Setup Mode; see Troubleshooting.
Step 6 — Turn on Secure Boot
Enforcing Secure Boot is a firmware setting, so it's done once, by hand, in the server's BIOS setup — through the provider's remote console, or by asking their support. After the next reboot:
kheeper hosts get myorg/vault
SecureBoot enforced
and the release shows as verified:
kheeper releases list myorg/vault
TAG IMAGE CREATED BOOTED SEAL
v1 (active) myorg/app@sha256:4f1c… 2026-10-11 09:12:44 2026-10-11 09:31:02 verified
verified means the host booted the digest the release was sealed to, with
Secure Boot enforced. sealed alone means it hasn't yet.
Living with sealed hosts
- Every release must be sealed. The registry refuses an unsealed release to a host that enforces Secure Boot, since the host couldn't boot it.
- Only your db key boots. A UKI signed with any other key is refused by
the firmware, and the host stops at the firmware's boot menu until someone
picks an older entry at the console.
releases create --sealrefuses a key that isn't your org's registered db certificate. - Option ROMs. With only your key in db, firmware won't load add-in
cards' UEFI drivers signed by Microsoft. If a host needs them, add
Microsoft's UEFI CA
with
kheeper secureboot add-db myorg --cert microsoft-uefi-ca.pem. Hosts pick up the new db with their next sealed release. Be aware that this also lets anything else Microsoft's CA signed boot, such as a Linux distribution's shim. - The upgrade agent is sealed too. The poll layer that keeps a host on its active release is part of the sealed filesystem, so a sealed host keeps the version it was sealed with until its next release.
- Sealed hosts always reboot fully into a new release. A soft reboot would keep the kernel the firmware verified for the previous release.
- Each release is sealed per host. Hosts have their own configuration, so their own digest and their own UKI, about 125 MB each.
Troubleshooting
| Symptom | Cause |
|---|---|
has not reported booting composefs |
The host was installed without --composefs, or hasn't checked in since it booted. |
is not built for composefs |
The image isn't built FROM public/composefs-base. |
SecureBoot setup after a sealed release |
The firmware was in Setup Mode but the keys didn't take. journalctl -u kheeper-secureboot on the host says why. |
SecureBoot off and the BIOS has no Secure Boot option enabled |
Some boards also need CSM disabled ("Boot mode: UEFI") before Secure Boot can be enabled. |
| The host doesn't come back after enabling Secure Boot | Something in the boot path isn't signed by your db key — most often an option ROM. Disable Secure Boot at the console, then see Option ROMs above. |