Kernel and rootfs images for microVMs, built for Flintlock / the Liquidmetal microVM stack.
The six "Build and publish" workflows above are triggered manually (
workflow_dispatch) rather than on every push, so their badges reflect the result of the last manual run, not continuous build status. The "Lint kernel configs" workflow runs automatically on every pull request that touches a.configfile.
This repository builds the kernel and root filesystem images consumed by Flintlock-managed microVMs. It does not contain a runnable application — it is a collection of Dockerfiles and Makefiles that each produce one OCI image, published to ghcr.io/liquidmetal-dev/*, which Flintlock then uses as the kernel and/or disk image source when creating a microVM.
Images are built for two hypervisors:
- Firecracker (
kernel-fc,kernel-k8s-fc) - Cloud Hypervisor (
kernel-ch,kernel-k8s-ch)
and two rootfs flavours:
- A general-purpose Ubuntu guest image with the guest-agent pre-installed
- An airgapped RKE2 node image, bundling RKE2 install artifacts for offline Kubernetes node bootstrap
MicroVMs booted by Flintlock have no mechanism to load kernel modules at runtime. Most boot straight into the kernel with no initrd. Flintlock's overlay volume mode boots with an initrd that Flintlock provides, and that initrd carries no modules either. Every driver a guest needs (virtio, networking, filesystem, etc.) must therefore be built directly into the kernel (=y), not built as a loadable module (=m).
This invariant is enforced twice:
- At build time, each kernel
Dockerfilefails the build if the merged kernel.configcontains any=mline. - In CI,
hack/check-no-modules.shruns against every*.configfile in the repo and is wired intolint-kernel-config.yml, which runs on any pull request touching a.configfile.
| Directory | Image | Description |
|---|---|---|
kernel-fc/ |
ghcr.io/liquidmetal-dev/firecracker-kernel |
Bare Firecracker-compatible kernel. Builds three variants: 6.1, 5.10, 5.10-no-acpi. |
kernel-k8s-fc/ |
ghcr.io/liquidmetal-dev/firecracker-kernel-k8s |
Firecracker kernel with additional netfilter/IPVS/bridge config needed for Kubernetes nodes (kube-proxy, CNI). Variants: 6.1, 5.10. |
kernel-ch/ |
ghcr.io/liquidmetal-dev/cloudhypervisor-kernel |
Bare Cloud Hypervisor kernel (6.2) plus a matching 6.2-headers image. |
kernel-k8s-ch/ |
ghcr.io/liquidmetal-dev/cloudhypervisor-kernel-k8s |
Cloud Hypervisor kernel (6.2) with the same Kubernetes networking config additions as kernel-k8s-fc. |
ubuntu/ |
ghcr.io/liquidmetal-dev/ubuntu |
Ubuntu 24.04 rootfs with networking, systemd and cloud-init configured, and guest-agent installed and enabled as a systemd service. |
rke2/ |
ghcr.io/liquidmetal-dev/node-rke2-airgapped |
Ubuntu 22.04-based RKE2 node rootfs, pre-loaded with RKE2 release artifacts for airgapped/offline installation. |
Each kernel image is built from real upstream linux-stable source, using kernel .configs sourced from the Firecracker/Cloud Hypervisor projects and merged with the additional config fragments in each directory's configs/ folder (kernel-k8s-ch/ keeps its fragments at the directory root).
Kernel images are published under a moving tag and a timestamped one, see Kernel image tags.
Every kernel also gets the erofs.config fragment, which enables the EROFS filesystem. Flintlock's overlay volume mode presents the root filesystem as a read-only EROFS image. The fragment is copied into each kernel directory because each directory is its own docker build context; hack/check-fragments-match.sh fails the lint when a copy is missing or the copies differ.
Every kernel build is published under two tags:
| Tag | Example | Behaviour |
|---|---|---|
<version>-<build id> |
6.1-20260929-1432 |
Names one build and is never overwritten. |
<version> |
6.1 |
Moves to the newest build each time one is published. |
The build id is the UTC time the build started, as YYYYMMDD-HHMM, so the tags of an image sort from oldest to newest. Variants keep their suffix ahead of the build id: 5.10-no-acpi-20260929-1432, 6.2-headers-20260929-1432. All the variants from one publish share a build id.
Use the short tag to follow the latest kernel, and a timestamped tag to stay on a known build or to go back to an older one:
ghcr.io/liquidmetal-dev/firecracker-kernel:6.1-20260929-1432
Each image also carries labels that say what is inside it:
| Label | Value |
|---|---|
org.opencontainers.image.version |
The timestamped tag the image was published as. |
org.opencontainers.image.created |
When the build started. |
org.opencontainers.image.revision |
The commit of this repository it was built from. |
dev.liquidmetal.kernel.version |
The linux-stable tag that was compiled, e.g. 5.10.245. |
docker buildx imagetools inspect ghcr.io/liquidmetal-dev/firecracker-kernel:6.1 \
--format '{{json .Image.Config.Labels}}'make push never replaces a timestamped tag that is already in the registry. If the tag holds a different image, nothing is pushed. If it holds the image being pushed, the tag is skipped, so a make push that failed part way can be run again. The publish workflows run one at a time for each image.
Every component follows the same make interface:
cd <component> # e.g. ubuntu, kernel-fc, rke2, ...
make build # docker build
make push # docker pushThe target registry defaults to ghcr.io/liquidmetal-dev and can be overridden:
make build REGISTRY=my-registry.example.comFor the kernel images, make build records the build id it used in out/build-id and make push publishes that build. Set BUILD_ID on both to choose the value yourself:
make build BUILD_ID=20260929-1432
make push BUILD_ID=20260929-1432Docker (with buildx) is the only required tool — kernel builds compile the actual Linux kernel inside the build container, so expect kernel-image builds to take a while.
kernel-fc/ Bare Firecracker kernel (6.1, 5.10, 5.10-no-acpi)
kernel-k8s-fc/ Firecracker kernel with Kubernetes networking config
kernel-ch/ Bare Cloud Hypervisor kernel (6.2)
kernel-k8s-ch/ Cloud Hypervisor kernel with Kubernetes networking config
ubuntu/ Ubuntu rootfs image with guest-agent
rke2/ Airgapped RKE2 node rootfs image
hack/ Repo-wide dev/CI scripts (e.g. check-no-modules.sh) and the shared kernel tagging
.github/workflows/ CI: per-image build-and-publish workflows, kernel-config lint
Apache License 2.0, see LICENSE.