Adding Linux firmware to Hadron
Hadron ships with a deliberately small firmware set to keep the base image lean. Most hardware works out of the box, but some devices (GPUs, Wi-Fi/Bluetooth chips, NICs, etc.) need extra firmware blobs.
The kairos-io/hadron-firmware project builds
the full upstream linux-firmware
tree, split into per-vendor/per-device pieces, and publishes it so you can add only the firmware
your hardware actually needs. It ships two kinds of artifacts that contain exactly the same
firmware files, so you can pick whichever way of consuming them fits your workflow:
- Firmware container images (OCI images) — one image per firmware folder, meant to be copied
on top of your Hadron image with a small
Dockerfile. Use this to bake firmware into a custom image, the same way the NVIDIA drivers page extends Hadron. - System extensions (sysexts) —
*.sysext.rawfiles that are loaded at boot bysystemd-sysextand can be added/removed on a running system, without rebuilding the OS image. This is the same mechanism described in Extending Hadron with extensions and the Kairos system extensions documentation.
A browsable list of every release and the images/sysexts it published is available on the project's GitHub Pages site. You can also look at the Releases page directly.
What gets published​
Each release corresponds to an upstream linux-firmware version (for example 20260622) and is
tagged with that version. The linux-firmware tree is split into targets, one per top-level
folder (very large folders such as intel and qcom are split further into per-subfolder pieces
plus a *-generic remainder, and loose files at the root are grouped into an uncategorized
target).
For every target you get:
- An OCI image:
ghcr.io/kairos-io/hadron-firmware/linux-firmware-<target>:<version>. Inside the image the files live under/usr/local/lib/firmware/.... - A sysext file attached to the release:
linux-firmware-<target>_<version>.sysext.raw.
For example, the amdgpu firmware for release 20260622 is published as:
- Image:
ghcr.io/kairos-io/hadron-firmware/linux-firmware-amdgpu:20260622 - Sysext:
linux-firmware-amdgpu_20260622.sysext.raw
The sysext files published on the releases are unsigned. If you run Trusted Boot you must build and sign them yourself — see Signed sysexts for Trusted Boot.
Option 1: Bundle firmware into your Hadron image (OCI images)​
This is the same pattern used by the Hadron
add-packages examples:
build a derived image FROM your Hadron base and COPY the firmware layer in.
Because the firmware images are FROM scratch images that already place files at
/usr/local/lib/firmware/..., you just copy their whole root (/) into your image root (/):
# Start from the Hadron image you already use.
FROM ghcr.io/kairos-io/hadron:main
# Copy in the firmware you need. Add as many COPY lines as you want.
COPY / /
COPY / /
Build it:
docker build -t my-registry.example.com/my-hadron:latest .
The image produced above is not a bootable/upgradeable Kairos image yet — it is just a Hadron
rootfs with extra firmware. A raw Hadron image cannot be used to upgrade a running Kairos node
directly. You first have to "kairosify" it (add the Kairos framework, init system integration,
etc.) with kairos-init, and then either push the
resulting image to upgrade to, or turn it into installable media (ISO, raw disk, etc.) with
AuroraBoot.
For example, run kairos-init as a build stage on top of the image above so the final image is a
proper Kairos image you can deploy or upgrade to:
FROM quay.io/kairos/kairos-init:latest AS kairos-init
# Turn the Hadron image with firmware into a bootable Kairos image.
FROM my-registry.example.com/my-hadron:latest
ARG VERSION=1.0.0
RUN /kairos-init --version "${VERSION}"
docker build -t my-registry.example.com/my-kairos:latest .
docker push my-registry.example.com/my-kairos:latest
Only the resulting my-registry.example.com/my-kairos:latest image (or the media produced from it
with AuroraBoot) can be used to install or upgrade a node. Refer to the
Kairos Factory documentation for the full set of kairos-init
flags and AuroraBoot invocations.
Tips:
- Pin to a specific firmware version tag (for example
:20260622) rather than a moving tag so your builds are reproducible. - Only copy the targets your hardware needs; the whole tree is large and there is no benefit to shipping firmware you will never load.
- Not sure which target holds your firmware? Check the release listing on the GitHub Pages site or browse the image list on a release.
Option 2: Add firmware as a sysext​
System extensions let you add firmware to an already installed system without rebuilding the OS
image. At boot systemd-sysext merges the extension's /usr/... contents into the running system
(read-only), and kairos-agent manages installing/enabling them. See the Kairos
system extensions documentation for the full picture, and
Extending Hadron with extensions for a Hadron-specific walkthrough of
building your own.
The base OS needs systemd 252 or newer (Hadron already satisfies this).
Install and enable with kairos-agent​
kairos-agent sysext install accepts https:, http:, file: and oci: URIs. The simplest
option is to point it at the *.sysext.raw file attached to a release:
# Download and install the firmware sysext from a release asset.
kairos-agent sysext install https://github.com/kairos-io/hadron-firmware/releases/download/20260622/linux-firmware-amdgpu_20260622.sysext.raw
# Enable it for the active boot profile and load it right now.
kairos-agent sysext enable --active --now linux-firmware-amdgpu
# Confirm it is enabled.
kairos-agent sysext list --active
Notes:
- The
enable/disable/removecommands accept a regex, so you can use a short name likeamdgpuinstead of the full filename. - Use
--commoninstead of--activeif you want the extension loaded in every boot profile (active, passive and recovery). - Drop
--nowif you only want the change to take effect on the next reboot. - To remove it again:
kairos-agent sysext disable --active --now linux-firmware-amdgpu(orkairos-agent sysext remove --now linux-firmware-amdgputo delete it).
Signed sysexts for Trusted Boot​
Under Trusted Boot the sysext signature is verified at boot
against the keys enrolled in the EFI firmware (PK/KEK/DB). The *.sysext.raw files attached to the
releases are unsigned examples and will be ignored on a Trusted Boot system.
To use firmware sysexts under Trusted Boot you must build and sign them yourself with the same private key/certificate you used to sign your EFI files, so they verify correctly on your machines. This is the same signing flow described in Extending Hadron with extensions and the Trusted Boot firmware sysext example.
The firmware.sh script in
the hadron-firmware repository supports this: it builds the firmware images locally and then
produces signed sysexts via AuroraBoot. This requires
docker and access to the docker socket.
# Build the firmware images and produce signed sysexts using your own keys.
./firmware.sh --build
./firmware.sh --sysext \
--private-key /path/to/your/db.key \
--certificate /path/to/your/db.pem
The signed *.sysext.raw files are written to the build/ directory. Install them on your nodes
the same way as above (for example with a local path):
kairos-agent sysext install file:///path/to/linux-firmware-amdgpu_20260622.sysext.raw
kairos-agent sysext enable --active --now linux-firmware-amdgpu
The keys under .github/keys/ in the hadron-firmware repository are test keys only and are
considered compromised. Never use them in production — always sign with your own Trusted Boot keys.
Building it yourself​
Everything is driven by the firmware.sh
script, which discovers the firmware layout from the built linux-firmware tree and generates the
Dockerfiles automatically. It needs docker (with buildx).
# Show all options.
./firmware.sh --help
# Only generate the Dockerfile.firmware (no build), to inspect the targets.
./firmware.sh --dockerfile-only
# Build every firmware image locally.
./firmware.sh --build
# Build a single target.
./firmware.sh --build --target amdgpu
# Build for a specific linux-firmware version and push to a registry.
./firmware.sh --build --push \
--repository ghcr.io/kairos-io/hadron-firmware \
--firmware-version 20260622
# Turn the built images into (optionally signed) sysexts.
./firmware.sh --sysext [--private-key KEY --certificate CERT]
Common flags:
| Flag | Description |
|---|---|
--dockerfile-only | Generate Dockerfile.firmware only, without building. |
--build | Build the firmware images. |
--sysext | Create sysexts from the built firmware images. |
--push | Push built images to the repository (requires --build). |
--target <name> | Build only the named target. |
--firmware-version <ver> | linux-firmware version to build. |
--repository <repo> | Destination image repository (default ttl.sh). |
--private-key <path> | Private key used to sign sysexts (Trusted Boot). |
--certificate <path> | Certificate used to sign sysexts (Trusted Boot). |
Releases (images, unsigned sysexts and the release listing on GitHub Pages) are produced
automatically by the CI workflows in the hadron-firmware repository when a version tag is pushed.