{"slug": "adding-debian-and-what-it-weighs", "title": "Adding Debian — and what it weighs", "summary": "Yoe, a build system originally designed for Alpine Linux, now supports Debian as a second backend, enabling projects to build both Alpine and Debian images side by side. The Debian backend provides access to nearly 69,000 packages but results in larger images (405 MB vs 85 MB for Alpine) and longer assembly times (100 s vs 10 s). This allows developers to mix distributions in a single project, using Debian for compatibility where needed and Alpine for lean workloads.", "body_md": "`[yoe]`\n\nstarted Alpine-first: musl, busybox, OpenRC, and `apk`\n\n. That stack is\nsmall, fast, and clean to wrap, and it remains the default for new projects. But\nnot every workload fits it. Some binaries are built for glibc and never ported\nto musl — CUDA, vendor camera and radio drivers, a lot of enterprise software.\nSome teams already run a Debian/Ubuntu-based fleet and want their edge devices\nto match it. And the apt ecosystem is vast.\n\nSo `[yoe]`\n\nnow has a second backend: Debian. The\n[latest video](https://youtu.be/16pxjZaw-Wo) walks through it. The same project\ncan build Alpine and Debian images side by side, choosing the distro per image.\nA unit’s source build runs in whichever toolchain the consuming image needs —\nmusl for an Alpine image, glibc for a Debian one — and produces a libc-correct\nbinary either way. You pick the platform the workload requires, image by image.\n\nUnder the hood, the Debian backend reuses the unit model Alpine already runs on.\n`[yoe]`\n\ntargets Trixie, Debian’s current stable release, and Debian main alone\nexposes nearly 69,000 packages — against roughly 25,000 in Alpine’s `main`\n\nand\n`community`\n\nrepos combined. Rather than ingest all of that up front, `[yoe]`\n\nparses the package index files on startup and materializes units on demand,\nexactly as it does for Alpine. Even against a feed that size, the TUI still\ncomes up in about a second.\n\nThe one place the two worlds diverge is naming. The same library often goes by a\ndifferent package name on each base — Python is `python3`\n\non Alpine but\n`python3.11`\n\non Debian — so a unit now carries per-distro build and runtime\ndependencies, and the build system resolves the right ones for the image it’s\nassembling. Some source packages needed adjusting to compile cleanly against\neither base, but once that’s done a single unit builds on both. Picking a\nplatform is then just a project setting: flip the default distro between Alpine\nand Debian and rebuild.\n\n`[yoe]`\n\nlets you reach for the base distribution without committing the whole\nproject to it. If you don’t have a hard reason for Debian — a glibc-only\nlibrary, a vendor binary, an existing Debian fleet — Alpine keeps the image\nsmall and the defaults work. If you do, Debian’s plumbing is there: feeds\nresolve, packages mirror, the rootfs assembles in a single `mmdebstrap`\n\npass,\nand the project’s own repo emits a signed index.\n\nThe pattern this enables is mixing the two in one project — a glibc Debian host image running vendor agents and a container runtime, with small musl Alpine application containers deployed inside it. The host pays for compatibility where it needs it; the workloads stay lean. You spend the extra megabytes exactly once, exactly where they earn their place, instead of across the whole device.\n\nDebian buys compatibility, and it costs space. To measure that honestly, we\nbuilt the same minimal image on each backend: the smallest thing that boots and\naccepts an SSH login, with no extra developer tooling on either side. Just a\nkernel, an init system, libc, a shell, the package manager, `sshd`\n\n, DHCP\nnetworking, and a login user. Same job, same `qemu-x86_64`\n\ntarget.\n\n| Backend | Packages available | Minimal `ssh-image` rootfs | Dev image assembly |\n|---|---|---|---|\n| Alpine | ~25,000 | 85 MB | ~10 s |\n| Debian | ~69,000 | 405 MB | ~100 s |\n\nDebian’s draw is the catalog — about 69,000 packages in `main`\n\n, against Alpine’s\n~25,000 across `main`\n\nand `community`\n\n. The cost is size and time: roughly 4.8×\non disk — the apples-to-apples platform floor for a real device you can log into\n— and close to 10× in assembly. The build-time column is the cached\nimage-assembly step for the dev image, a notch up from this minimal one; the\nsections below break down where each gap comes from.\n\nTwo things, and most of it is the kernel.\n\n**The kernel.** Most of the gap is the stock `linux-image-amd64`\n\npackage — its\nmodule tree alone is about 107 MB, because a distribution kernel ships a driver\nfor every machine it might ever run on. Alpine here boots a kernel `[yoe]`\n\nbuilt\nfrom source, tailored to the target, so its modules are a few megabytes rather\nthan a hundred-plus. This part of the gap isn’t really “Debian vs Alpine” — it’s\n“stock distribution kernel vs tailored build,” and a production Debian image on\n`[yoe]`\n\ncan swap in a tailored kernel too. Out of the box, though, the distro\nkernel brings everything.\n\n**The userland.** The remainder is the platform itself: glibc and its multiarch\nlibraries, systemd and NetworkManager, the full apt and dpkg stack, complete\ncoreutils instead of busybox applets, locales, udev rules. musl plus busybox\nplus OpenRC is simply a lighter foundation, and on a device that difference\ncompounds.\n\nThe two images are also assembled differently, and it comes down to what a\npackage *does* when it installs.\n\nAssembling the Alpine image is essentially one step: `apk`\n\nextracts the packages\ninto the root filesystem, resolves their dependencies, and checks for file\nconflicts. Alpine packages are largely self-contained — the install-time logic\nis minimal, mostly busybox wiring up its own applet symlinks. Few moving parts,\nand it’s fast.\n\nA Debian `.deb`\n\ncarries maintainer scripts — `preinst`\n\n, `postinst`\n\n— that have\nto run to finish the install: creating users, enabling systemd services, running\n`ldconfig`\n\n, registering alternatives. So `[yoe]`\n\nassembles the Debian rootfs\nwith `mmdebstrap`\n\ndriving `apt`\n\nand `dpkg`\n\n, then runs every package’s scripts to\nreach a fully configured state. And because those scripts assume a complete base\nsystem is already in place, assembly carries ordering subtleties the Alpine path\nnever hits. A concrete one: `openssh-server`\n\n’s post-install reaches for `awk`\n\nbefore the package that provides it has been configured, so the assembler\npre-stages the `awk`\n\nalternative ahead of time to keep the script from failing.\nFor a foreign architecture, all of those scripts run under QEMU emulation as\nwell.\n\nNone of this is a knock on Debian — that maintainer-script model is exactly what\nmakes the broad apt ecosystem drop in and *just work*, users and services\nconfigured the way the package author intended. It’s the same tradeoff as the\nsize: the richness that earns Debian its place is also what makes it heavier to\nassemble. Alpine does less at install time, so there’s less to do at build time.\n\nIt shows up on the clock, too. With every dependency package already built and\ncached — so the timer captures only the image-assembly step, not the source\nbuilds behind it — reassembling the dev image (a working image with editors and\ndiagnostics, a notch up from the minimal one measured above) on the same\n`qemu-x86_64`\n\ntarget takes about **10 seconds on Alpine and roughly 100 on\nDebian**: close to a 10× gap. Alpine’s side is a single `apk`\n\nextract; Debian’s\nis `mmdebstrap`\n\nplus `dpkg`\n\nconfiguring every package and running its maintainer\nscripts, under QEMU for a foreign architecture. The Debian time also wanders\nmore from run to run (90–120 s), because it leans on apt and dpkg doing real\nwork rather than a near-deterministic unpack.\n\nDebian support is experimental but real: the image measured above boots and accepts SSH, so it’s a device you can log into and work on, not a paper target. The build path is solid and the size numbers are measurements, not estimates. There’s still plenty to harden, and honest comparisons — like this one — are how we keep the tradeoffs in view as it matures.\n\nHardening the Debian path from experimental toward production — tailoring the\nkernel down from the stock one, and proving it out across more targets. The\n[Videos page](https://yoebuild.org/videos/) has the full walkthrough set.\n\nIf you have a workload that needs glibc, we’d love to hear how it goes —\n[open a discussion](https://github.com/orgs/yoebuild/discussions) or send a\n[note](mailto:info@yoebuild.org?subject=%5Byoe%5D%20Debian%20feedback). To\nfollow `[yoe]`\n\nas it grows,\n[star and watch the repo](https://github.com/yoebuild/yoe) and\n[subscribe for updates](https://yoebuild.org/atom.xml) — there’s an email signup at the foot of\nevery page, too.", "url": "https://wpnews.pro/news/adding-debian-and-what-it-weighs", "canonical_source": "https://yoebuild.org/blog/adding-debian/", "published_at": "2026-06-03 00:00:00+00:00", "updated_at": "2026-06-18 17:40:37.440003+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Yoe", "Alpine Linux", "Debian", "CUDA", "mmdebstrap", "qemu-x86_64"], "alternates": {"html": "https://wpnews.pro/news/adding-debian-and-what-it-weighs", "markdown": "https://wpnews.pro/news/adding-debian-and-what-it-weighs.md", "text": "https://wpnews.pro/news/adding-debian-and-what-it-weighs.txt", "jsonld": "https://wpnews.pro/news/adding-debian-and-what-it-weighs.jsonld"}}