A build system for Debian device images — for your laptop, SBC, tablet, or TV box.
boot2deb describes a device as layered TOML config and builds a bootable Debian image from it: compile the kernel and bootloader (or install the distro's), bootstrap the rootfs, assemble the disk. A build is a point across axes — device × kernel × u-boot × suite × features × layout — resolved from those layers and pinned into one committed lockfile.
It is a typed, unit-tested Rust workspace, and every axis and layer merge is validated before a build runs. It ships recipes for six boards, which are both usable images and worked examples of unlike hardware: what makes a Chromebook different from a compute module is visible in the config rather than buried in a script.
Under active development, with breaking changes between commits. Builds run mostly on an x86_64 Pop!_OS laptop and a few on a Turing RK1 running an image boot2deb built.
What has actually been booted is a per-recipe, per-pin claim, and it is generated from the locks rather than written by hand — so read it from the tool, not from prose:
boot2deb support-matrixEach recipe carries a status — validated (an image from these exact pins booted on the
hardware), expected (derived from a validated sibling along an axis not expected to change
the outcome), or experimental (under active bring-up) — plus the date the claim was last
established. Re-pinning retires a validated claim, because the evidence was about the pins
that moved. The published rendering is the
support matrix;
nothing here restates it. No board has been through a full sweep of its peripherals, and an
absent claim is not a claim that the hardware does not work.
boot2deb was developed by AI, primarily Claude Code. Human involvement was mostly limited to setting project goals and validating images on hardware. This is a side project to support a hobby and comes with no guarantee of quality, accuracy, or update frequency.
- Rootless. Cross-architecture package builds and the Debian bootstrap run in a
rootless, in-process user-namespace sandbox (plus
qemu-userwhen cross-building), and the disk image is assembled with no root and no loop devices: GPT tables,.xzcompression, and the ext4 filesystem are all pure Rust. An x86_64 host builds an arm64 image withoutsudo. - Reproducible. The
.lockpins every input — source commits, firmware-blob hashes, and the solved apt manifest — and each image ships a provenance manifest recording exactly what went into it, down to the boot2deb commit that built it. Package churn in a rolling suite is pinned againstsnapshot.debian.orgon demand. See Reproducibility.
The six shipped configurations are deliberately unalike: between them they exercise every axis of the model, so the nearest example to a board you care about is usually one of them.
| Board | SoC / arch | What this example shows |
|---|---|---|
| Turing RK1 | RK3588 / arm64 | The full pipeline: a patched mainline kernel, u-boot written into the disk's raw gap, and an optional Rockchip media userspace (MPP + RGA + ffmpeg-rk) built in a target-arch sandbox |
| H96 MAX M9 | RK3576 / arm64 | A board mainline does not know: its device tree ships with the config, its Wi-Fi driver is an out-of-tree kmods/ layer, and its u-boot is its own axis — including recipes whose only deliverable is a maskrom-streamable bootloader |
| ASUS Chromebook C201 | RK3288 / armhf | The opposite extreme: 32-bit, Debian's own kernel, ChromeOS firmware in SPI. It compiles nothing, its lock pins nothing from git, and the deliverable is a vboot-signed kernel in a ChromeOS partition — with A/B slots, so a bad kernel upgrade costs one reboot |
| ASUS C100P · Chromebit CS10 | RK3288 / armhf | What a board costs once its family is here: one device file each, no overlay, no kernel, no code — everything shared lives on the SoC layer |
| RK3576 EVB1 v10 | RK3576 / arm64 | The reference board beside the retail one, sharing a SoC layer and a kernel while carrying none of the TV box's peripherals |
Hardware video transcode is the RK1's headline capability, and it has been measured on a
boot2deb-built image: the h264_rkmpp and hevc_rkmpp encoders produce correct streams,
and hardware decode through -hwaccel v4l2request cuts decode CPU cost by 53x at 1080p
and up to 143x at 4K. What is not yet exercised end to end is Jellyfin driving that path
itself — see Accelerated Jellyfin.
On the RK3576 the picture is narrower: decode and 2D are driven, encode has no mainline
driver at all, and hardware H.264 decode is not yet reliable — see
H96 MAX M9.
Build the base Turing RK1 image on an x86_64 or arm64 Debian/Ubuntu host. The build is
rootless — no sudo.
-
Install Rust (rustup.rs), clone this repo, and install the binary:
cd boot2deb cargo install --path crates/cli # puts `boot2deb` on your PATH
The crate is
boot2deb-cli; the binary it installs isboot2deb. Every command below — and every hint the tool itself prints — assumes it is onPATH. Working from a checkout without installing, prefix them withcargo run -p boot2deb-cli --. -
Ask
doctorwhat your host is missing. It probes for every build tool the recipe will actually invoke and prints the exact install command for your distro:boot2deb doctor turing-rk1/forky
Run the lines it reports, then re-run until every check passes.
-
Build. This compiles the kernel and u-boot, bootstraps the Debian rootfs, and writes a bootable disk image (tens of minutes cold; cached after):
boot2deb build turing-rk1/forky
The final lines print the image path under
build/turing-rk1/forky/artifacts/and a unique first-boot password for userdebian— note it down, or authorize your SSH key in the recipe and skip typing it. For hardware video transcode, buildturing-rk1/media-accel-forkyinstead. -
Flash it. This is board-specific — for the RK1 it is the Turing Pi BMC (
tpior the web UI), or a removable card. See Turing RK1.
Full walkthrough: Getting started.
A shipped recipe is a starting point, not a ceiling. list-recipes shows what is authored;
most changes need no new file at all.
Compose features a-la-carte. Name any selection on update/build and it is pinned and
built as its own point, with its own lock, beside the recipe it starts from:
boot2deb update turing-rk1/forky --feature media-accel-rockchip --feature jellyfin
boot2deb build turing-rk1/forky+media-accel-rockchip+jellyfinKeep your own work out-of-tree. An overlay directory holds your devices, kernels, and
recipes, wins over the shipped tree name-for-name, and takes the locks update writes — so
there is nothing to fork and nothing to rebase. A base.toml there is also where your own
SSH keys belong, so every image you build authorizes you without editing the shipped tree:
the account, sudo, and SSH keys.
The tutorials take these in order:
- Adapting a shipped recipe — a different suite, feature set, or localization, from a build flag up to a device of your own.
- Moving a board to a newer kernel — measure whether a patch series survives a kernel you have not adopted (it changes no pin), encode the boundary, then adopt it.
- Authoring a recipe — name a build point, and declare what it has been taken through.
- Adding a board
— bring up hardware that is not here yet, starting from
boot2deb new-device.
- Config model — the hardware stack resolves
arches ← socs ← boot-methods ← devices, with the kernel, the u-boot series, out-of-tree modules (kmods/), and rootfs features (features/) as orthogonal axes. Config model. - Recipes and locks — a recipe pins a build point by name;
updateis the only command that consults upstream and writes a sibling.lock;buildreads only that lock. - Kernel patches — version-coupled patch series and kconfig fragments live on the kernel
axis and are applied behind a verify-applies gate. A series declares which kernel versions
it claims, and
verify-patches --kernelmeasures a version it does not. Adding a patch. - Not every board needs every stage — a build compiles a kernel only if the board needs one of its own, and a bootloader only if the board's firmware is ours to make. The model states what is true of each board rather than making them look alike.
The full documentation is published as a book at
gregordinary.github.io/boot2deb. The sources
live in docs/; build them locally with mdbook serve docs.
- Introduction
- User guide — Getting started, Producing images, Upgrading the kernel, Locale, timezone, and keyboard, The clock and time sync, The account, sudo, and SSH keys, Data volumes, Accelerated Jellyfin
- Tutorials — Adapting a shipped recipe, Moving a board to a newer kernel, Authoring a recipe
- Boards — Turing RK1, H96 MAX M9, RK3576 EVB1 v10, ASUS C201, ASUS C100P, ASUS Chromebit CS10
- Reference — Config model, What the base image contains, CLI, Every flag, Support matrix, The on-image self-test, Trying an image before flashing, RK3576 u-boot images, Overlays, Image identity, The maskrom loader, Reproducibility
- Contributing — Adding a board, Adding a patch
crates/core typed model, layer resolution + validation, patch-series / lock /
kconfig formats (pure, unit-tested)
crates/engine Linux side effects: git shell-outs, lock resolver, patch verify gate,
kernel-config generation, the compile stages, the rootfs + image nodes,
and the host preflight behind `doctor`
crates/cli the boot2deb binary
arches/ socs/ boot-methods/ devices/ kernels/ kmods/ features/ recipes/
config layers (TOML)
blobs/ fragments/ vendored blobs, kconfig
docs/ the mdBook
boot2deb is licensed under the GNU General Public License v3.0 or later — see
LICENSE. Vendored third-party components (the Rockchip rkbin firmware blobs,
the boot and kernel-hook scripts, and the Debian archive keyring) keep their own licenses;
see THIRD-PARTY-NOTICES.md.