kbake is a declarative Linux kernel builder. You describe the kernel you
want in a Kernelfile (YAML) β the source to start from, the config options
and modules to enable or disable, and files to add or remove β and kbake
builds it, reproducibly, for one or more architectures.
The build result is packaged as an OCI artifact
(a kernel binary plus a compressed modules.tar.gz, per architecture) and
stored in a local OCI store, from where it can be tagged, listed, exported or
pulled from a registry.
- Declarative: a single
Kernelfiledescribes source, config, modules and file changes β think Dockerfile, but for kernels. - Multi-arch: build for several architectures in one invocation; results are grouped under a single OCI image index.
- Reproducible: the compile phase runs with a pinned build environment
(
SOURCE_DATE_EPOCH=0, fixedKBUILD_BUILD_*values,TZ=UTC,LC_ALL=C) so identical inputs produce identical artifacts. - Fast: fetched kernel sources are cached go-module-style and shared between builds; compilation is accelerated with ccache (enabled by default).
- Conditional: per-architecture behavior via a small GitHub-Actions-style
expression language (
if: ${{ arch == 'arm64' }}) and$variablesubstitution.
Requires Go 1.26+.
$ go install github.com/ironcore-dev/kbake@latestOr build from source:
$ git clone https://github.com/ironcore-dev/kbake.git
$ cd kbake
$ make build # produces bin/kbakeCreate a Kernelfile (see examples/Kernelfile for a
complete one):
from: git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux@v7.1
options:
enable:
- EFI
- DEVTMPFS
- DEVTMPFS_MOUNT
- BPF
modules:
builtin:
- VIRTIO
- VIRTIO_NET
- VIRTIO_BLKBuild it for the host architecture and tag the result:
$ kbake build . -t my-kernel:latest
built sha256:9b1dβ¦
tagged my-kernel:latestList stored images and extract the kernel binary:
$ kbake image ls
IMAGE ID ARCH
my-kernel:latest 9b1d3f2a1c4 arm64
$ kbake get kernel my-kernel:latest -o kernelTo share an image, push the local store's artifacts with your favorite OCI
tooling; kbake get kernel can pull them back from any registry (using your
Docker credentials, see below).
A Kernelfile may be YAML or JSON (documents starting with { are treated as
JSON). The top-level fields:
| Field | Description |
|---|---|
from |
Base source: scratch or git://<host>/<path>@<ref>. |
options |
Shorthand config options to enable (=y) / disable (=n). |
modules |
Shorthand modules to enable (=m), make builtin (=y) or disable (=n). |
files |
Shorthand for adding files from the build context into the tree or deleteing files from the tree. |
actions |
Ordered list of fine-grained actions, optionally guarded by if conditions. |
git://<host>/<path-to-repo>@<ref>β fetch a git repository at the given tag, branch or commit, e.g.git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux@v7.1. Fetched trees are cached under--cache-dirusing a go-module-style layout (<host>/<path>/<repo>@<version>), so re-builds only pull the delta.scratchβ start from an empty tree.
The shorthand fields expand to config changes and file operations applied
before everything in actions:
options:
enable: [BPF, EFI] # -> CONFIG_BPF=y, CONFIG_EFI=y
disable: [DEBUG_INFO] # -> CONFIG_DEBUG_INFO=n
modules:
enable: [BRIDGE] # -> CONFIG_BRIDGE=m
builtin: [VIRTIO_NET] # -> CONFIG_VIRTIO_NET=y
disable: [SOUND] # -> CONFIG_SOUND=n
files:
- add: { src: patches/fix.patch, dst: patches/fix.patch } # from build context into tree
- delete: { path: drivers/staging/rts5208 } # remove from treeSymbol names are given without the CONFIG_ prefix.
For everything the shorthands don't cover, actions gives full control.
Each entry β or a whole group of entries β can be conditional:
actions:
- if: ${{ arch == 'arm64' }}
actions:
- setConfig: { name: ARM64_64K_PAGES, value: Yes }
- addFile: { src: firmware/$arch, dst: firmware }
- setConfig: { name: CMDLINE, value: Yes } # unconditional
- deleteConfig: { name: WATCHDOG }Available actions: setConfig (value Yes / No / Module),
deleteConfig, addFile, deleteFile. File src paths are resolved inside
the build context, dst/path inside the kernel tree; both are protected
against escaping their respective roots.
String fields support $variable substitution and ${{ ... }} expressions,
modeled after GitHub Actions. The build provides the arch variable.
Supported operators: == != < <= > >= && || ! and parentheses; literals:
strings, numbers, true / false / null.
actions:
- if: ${{ arch == 'amd64' || arch == 'arm64' }}
actions:
- addFile: { src: config/$arch.fragment, dst: .kbake-fragment }kbake <command> [flags]
Global flag: --local-dir β local OCI store (default ~/.kbake/repo).
Build a kernel from a Kernelfile. CONTEXT is the build context directory
used to resolve files:/addFile sources.
| Flag | Default | Description |
|---|---|---|
-f, --file |
Kernelfile |
Path to the Kernelfile (- reads stdin). |
--arch |
host arch | Comma-separated target architectures (e.g. amd64,arm64). |
-t, --tag |
β | Tag(s) to apply to the result in the local store. |
-j, --jobs |
-1 (all CPUs) |
Parallel make jobs. |
--no-ccache |
off | Disable ccache. |
--ccache-dir |
~/.cache/kbake/ccache |
ccache directory. |
--work-dir |
~/.cache/kbake/work |
Per-build work directory root. |
--keep-work |
off | Keep the work dir after the build (path printed to stderr). |
--cache-dir |
~/.cache/kbake/mod |
Kernel source cache (go-style layout). |
--temp-dir |
system temp | Directory for build logs. |
The build runs defconfig, applies your config changes, resolves dependencies
with olddefconfig, compiles, runs modules_install and packs the result as
an OCI artifact with two layers β the kernel binary and modules.tar.gz β
per architecture, plus an image index tying the architectures together. The
index digest is printed on success. On failure, the full build log path is
printed to stderr.
Extract the kernel binary from an image. Images are resolved from the local
store first; if absent, the image is pulled from the referenced registry
(authenticating via your Docker config, including credential helpers such as
docker-credential-osxkeychain).
| Flag | Default | Description |
|---|---|---|
-o, --output |
stdout | Write to a file instead of stdout. |
-a, --arch |
host arch | Kernel architecture to extract. |
--plain-http |
off | Use plain HTTP when pulling from a registry. |
List images in the local store (tag, short ID, architectures).
Remove tags from the local store and garbage-collect unreferenced content.
Print the kbake version.
Exit codes: 0 success, 1 runtime error (bad Kernelfile, build
failure, β¦), 2 usage error (unknown command, bad flags).
amd64, 386, arm64, arm, mips, mips64, ppc64le, riscv64,
s390x.
Native builds use the host toolchain; cross builds expect the matching
<triplet>-gcc plus binutils (as, ld, ar, nm, objcopy, objdump,
strip) on PATH β e.g. aarch64-linux-gnu-* for arm64. Build host
requirements: make, git, a C toolchain, and optionally ccache.
Artifacts use dedicated media types:
| Content | Media type |
|---|---|
| Artifact type | application/vnd.ironcore.kbake.v1+json |
| Kernel layer | application/vnd.ironcore.kernel.v1 |
| Modules layer | application/vnd.ironcore.kernel.modules.v1.tar+gzip |
| Config | application/vnd.ironcore.kbake.config.v1+json |
$ make build # build bin/kbake
$ make test # run tests (with coverage)
$ make lint # run golangci-lint
$ make check # add license headers, fmt, lint, testSee make help for all targets.
We'd love to get feedback from you. Please report bugs, suggestions or post questions by opening a GitHub issue.
Copyright 2025 SAP SE or an SAP affiliate company and IronCore contributors. Please see our LICENSE for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available via the REUSE tool.
