Every technician ends up with a USB stick that boots something. For a long time the good answer was Parted Magic, and it’s still an excellent product, but it’s commercial: currently around $17 for a single version that doesn’t expire, $59 a year, or $199 for a lifetime license. SystemRescue is free and genuinely capable, but it leans command-line and assumes you know what you’re reaching for.
There’s a third option that gets overlooked, which is that Debian ships a first-class tool for building your own bootable image, and it is not especially hard to use. live-build takes a directory of configuration and compiles it into a hybrid ISO that boots on BIOS and UEFI, from USB or optical media. You pick the packages, the desktop, the branding, and the boot menu. What you get out is yours, costs nothing, and contains exactly the tools you actually use.
The worked example throughout is MaxxRescue, a Debian 13 (Trixie) rescue image built for the NewMaxx community: Xfce desktop, partitioning and cloning and recovery tooling, ZFS support, SSD diagnostics, and guided launchers for the operations that are easy to get wrong. The current build is 1.6 GB with 1,368 packages.
But this guide is about the process, not that image. You may not want my tool selection, my launchers, or my scripts. Someone building a minimal 400 MB CLI recovery stick and someone building a full graphical workstation are running the same method with different inputs. What transfers is how you structure the config, how you handle the packages Debian doesn’t ship the way you need them, and above all how you test the result, which is the part every live-build tutorial I’ve read simply stops short of and which is where a project like this genuinely spends its time.
Important: This image is not finished, and the guide says so throughout rather than pretending otherwise. What remains is iteration: build, boot, test, fix, rebuild. That loop is well understood here and no longer teaches me anything, which is exactly why the method is worth writing down now. Step 7 is an honest accounting of what is validated and what is not.
Decide What You’re Building
Almost every later decision is downstream of a handful made at the start, so make them deliberately.
Why live-build rather than the alternatives. You can remaster an existing ISO by unpacking its squashfs, chrooting in, changing things, and repacking. It works, and it produces something nobody can reproduce, including you in six months. Cubic is a friendly GUI over roughly that approach and is a reasonable choice for a one-off. live-build is different in kind: your configuration is a directory tree of package lists, files, and hooks, and the ISO is a build artifact. Delete the ISO and rebuild it and you get the same thing. That property is why it’s worth the extra structure, and it’s why the phase discipline in Step 3 is even possible.
Stable versus rolling. An Arch-based image gets newer tools; a Debian stable base gets a kernel and headers that stay paired for years, which matters enormously the moment you need out-of-tree kernel modules. Rescue images are also the last place you want a surprise, since you reach for them when something is already broken.
The decisions worth writing down before you start, and the ones this build made:
| Decision | Choice | Why |
|---|---|---|
| Build system | live-build |
Debian-native, declarative, reproducible |
| Base | Debian 13 (Trixie) stable | Predictable kernel/header pairing |
| Desktop | Xfce | The rescue-distro default; light, around 300 MB of RAM |
| Architecture | amd64 only | 32-bit rescue hardware is no longer a real case |
| Boot | GRUB for UEFI, syslinux for BIOS | Hybrid ISO, both firmware paths |
| Filesystem | squashfs plus overlay | Compressed read-only base, writable overlay |
| Persistence | None in v1 | Real complexity for a feature most rescue use doesn’t need |
| Locale | en-US default | Documented customization rather than an assumption |
The persistence decision is the one people fight. A live image with no persistence forgets everything at reboot, which is correct for a rescue tool used on other people’s machines and inconvenient if you want it as a portable workstation. Deciding this at the start is much cheaper than retrofitting.
Scope realism. Parted Magic is a large, mature project with years of work in it. If you set out to match it feature for feature, you will not finish. Most people need considerably less: the tools they personally reach for, arranged so they can find them. Build that instead.
The Skeleton
A live-build project is a directory tree. The single most useful mental model is that the tree is the source and the ISO is the output, so anything you want in the image has to be expressed somewhere in the tree rather than done by hand.
The entry point is auto/config, a script that records how the image is configured. Running it writes out the build configuration; committing it means anyone can reproduce your setup:
#!/bin/sh
set -e
lb config noauto \
--distribution trixie \
--archive-areas "main contrib non-free non-free-firmware" \
--binary-images iso-hybrid \
--bootloaders "grub-efi,syslinux" \
--linux-flavours amd64 \
--memtest memtest86+ \
--security true \
--updates true \
--firmware-binary true \
--firmware-chroot true \
--iso-application "MaxxRescue" \
--iso-publisher "MaxxRescue" \
--iso-volume "MaxxRescue" \
"${@}"
Two of those flags carry more weight than they look like. --archive-areas pulling in contrib and non-free is what makes ZFS and a few other tools available at all, and non-free-firmware plus the two --firmware-* flags are what stop your image booting on a laptop with no working WiFi. A rescue image that can’t get on the network is dramatically less useful, and firmware blobs are how you avoid that.
The parts of config/ that do the work:
package-lists/*.list.chrootare plain text lists of package names, one per line, comments allowed. This is the bulk of what your image is.packages.chroot/holds.debfiles you supply yourself, for anything the repos don’t have in the form you need. Step 4.includes.chroot/is a filesystem overlay. Whatever you put here appears at the same path inside the image, soincludes.chroot/usr/local/bin/foobecomes/usr/local/bin/foo. This is where custom scripts, default desktop configuration, and branding live.hooks/are scripts that run during the build, inside the chroot. Use these for anything that has to be executed rather than copied: enabling and disabling services, generating manifests, deleting things to save space.includes.binary/is the same idea for the ISO itself rather than the installed filesystem, which is where boot menu configuration goes.
Everything after this is filling those directories in.
Package Lists, Phases, and Preflight
The obvious approach is one enormous package list. Do not do that, for a reason that only becomes obvious after your first long build fails at the end.
Split by theme. This build uses eight lists: a base with the core system and Xfce, then disk tools, clone and recovery, erase and security, network, diagnostics, system info, and extras. Each is independently comprehensible, and you can look at one and ask whether you actually use these.
Then gate the build in phases. A full build of an image this size takes a long time, and a failure at the end tells you almost nothing about which of eight lists caused it. So the build script takes a phase argument and enables lists cumulatively: phase1 is base only, phase2a adds disk tools, and so on to full. Each phase gets built, booted, and checked before the next one is enabled.
That structure buys you three things. Failures are attributable, because only one list changed. Image size is attributable, since you can watch it grow per phase and see what actually cost you. And a broken component can be deferred rather than blocking everything, which matters when something like ZFS is fighting you.
Each phase gets an explicit exit criterion, written before the build rather than after:
- Phase 1: boots on BIOS, boots on UEFI, desktop loads
- Phase 2A: GParted launches,
parted,gdisk,sgdisk,mdadm,lvmall callable - Phase 2B: Clonezilla,
testdisk,ddrescue,fsarchivercallable - Phase 2D:
fio --versionreports the version you built, diagnostic tools callable - Phase 2E: zero failures from both smoke test scripts
Preflight before you build. The cheapest possible failure is a package name that doesn’t resolve, and the most expensive way to discover it is forty minutes into a build. A preflight script that walks the enabled lists and checks every package resolves against the target repository turns that into a five-second failure. Write it early. Typos, renamed packages, and things that exist in sid but not in stable are all common enough to make this pay for itself immediately.
How AI can help
Curating tool lists is a good use of an assistant, with one caveat. Ask it what belongs in a category (say, data recovery tools available in Debian stable) and you'll get a solid starting list including things you'd have forgotten. What it will also do is confidently name packages that don't exist under that name in your target release, or that live only in sid. That is exactly what the preflight script exists to catch, so let the two work together: generate broadly, then let preflight tell you the truth. It's also worth asking it to write the preflight script itself, since the logic is mechanical and it will handle the phase-aware list enabling more carefully than you will at midnight.
When Stable Doesn’t Have What You Need
Two versions of the same problem, and a rescue image tends to hit both.
A package newer than the repo ships
Trixie ships FIO 3.39. This build needs 3.42 for specific storage benchmarking work, so the version in the repo isn’t sufficient. The wrong fix is installing build-essential into the image and compiling at build time, which bloats the ISO with a toolchain nobody needs at runtime. The right fix is to build a .deb once, on the host, and drop it into config/packages.chroot/ where live-build installs it like any other package.
Build it in a clean chroot of the target release, not on your workstation. This is the trap, and it is quiet. Compile on a machine with different library versions and the binary links against libraries that exist there and not in the image. The build succeeds, the package installs, and the tool fails at runtime inside the live environment with a missing-library error that gives you no hint about where it came from.
sudo debootstrap trixie /tmp/trixie-chroot http://deb.debian.org/debian
sudo chroot /tmp/trixie-chroot apt install -y build-essential libaio-dev liburing-dev zlib1g-dev pkg-config checkinstall dpkg-dev wget ca-certificates
Then build inside it, and gate the packaging on two checks before you trust the result. First, run ldd against the compiled binary and refuse to package if anything reports not found, which catches the host-library problem at the moment it’s cheap. Second, if the tool has a config or job format, parse-check a real file with it, since a binary that runs --version fine can still be missing a feature you compiled for. Save the ldd output alongside the .deb as a build artifact; when something misbehaves in the live image months later, that file is the fastest way to tell whether the binary or the environment changed.
An out-of-tree kernel module
ZFS is the standard example and it is genuinely awkward, because a DKMS module must be built against the exact kernel in the image. Getting that right means naming the whole chain explicitly in the package list rather than hoping dependencies resolve it: zfsutils-linux, zfs-dkms, dkms, and linux-headers-amd64 together. In this build that produced zfs-dkms 2.3.2 against kernel 6.12.86+deb13, with headers matching.
Secure Boot is the caveat that will bite your users rather than you. Debian’s Secure Boot policy blocks unsigned out-of-tree modules, and a DKMS-built ZFS module is unsigned. modprobe zfs fails on a machine with Secure Boot enabled, and the error does not say “Secure Boot,” it says the module could not be inserted. Either document it plainly, disable Secure Boot for those workflows, or set up module signing with MOK enrollment. The cheap mitigation is having your smoke test report mokutil --sb-state so the state is visible in the output rather than something the user has to suspect.
Important: Decide in advance which components are allowed to break the build and which are not. ZFS here is a soft gate: the smoke test warns if it fails but does not fail the build, and the plan was explicitly to ship without ZFS rather than let a DKMS problem hold the entire image hostage. Deciding that before you’re three hours into debugging is the difference between shipping a slightly smaller image and not shipping. Write down which pieces are load-bearing and which are nice to have.
How AI can help
The custom .deb build script is an excellent handoff: it's mechanical, it has real gotchas, and the gotchas are well documented. Ask for a script that builds inside a debootstrap chroot of a named release, verifies with ldd before packaging, normalizes the output filename with dpkg-name, and writes a checksum manifest. Then ask it specifically what happens if the script is run on the workstation instead of in the chroot, because a build script that fails loudly in that case is much better than one that quietly produces a broken package. For the DKMS side, it's good at explaining why a module failed to load when the error text doesn't mention the actual cause, which is most of the time with Secure Boot.
Customizing the Environment
Two mechanisms cover nearly everything: includes.chroot copies files in, hooks run commands. Reach for the first whenever you can, because a file is easier to inspect and diff than a script that edited something.
Files. Desktop defaults, panel layout, and wallpaper go in includes.chroot/etc/skel/, which becomes the live user’s home directory. Custom scripts go in includes.chroot/usr/local/bin/. Reference material, branding assets, and templates can live under /usr/share/ in your own directory.
Hooks. Anything that has to execute. Service configuration is the common case, and it’s worth being deliberate about defaults on a rescue image:
#!/bin/bash
set -e
systemctl disable ssh.service 2>/dev/null || true
systemctl enable NetworkManager.service 2>/dev/null || true
Note the || true. During early phases the package providing a service may not be installed yet, and a hook that fails hard on a missing unit breaks builds for no good reason.
Two more hooks earn their place. One generates a manifest into the image recording the package list, key tool versions, the kernel version, and a build timestamp, so a booted image can tell you exactly what it is. Read the kernel version from the installed package rather than uname -a, which reports the build host’s kernel and will mislead you. The other is a cleanup pass that removes non-English locales and documentation while keeping man pages for the tools people will actually need offline, since a rescue image is used in exactly the situations where you can’t look something up.
SSH deserves its own decision. A rescue image with SSH enabled by default is a machine on someone else’s network accepting connections with a known live-user password. Ship it disabled and let the user opt in with a boot parameter. Two details matter: the service handler must regenerate host keys at boot, because an ISO that ships build-time host keys hands the same key to every copy in existence, and Debian Trixie defaults to key-only authentication, so password login needs an explicit sshd_config.d drop-in. That second one was found on real hardware, not in QEMU, and is a good illustration of Step 6.
The boot menu lives in includes.binary/boot/grub/grub.cfg for UEFI, with entries for the normal boot plus the ones that get you out of trouble: copy-to-RAM so you can remove the USB, safe graphics for machines that won’t come up otherwise, a serial console entry, and your SSH opt-in entry.
Tip: Treat boot menu paths as post-build fixups rather than something you get right in advance. Kernel and initrd paths depend on live-build output, and memtest is especially variable, since Debian ships architecture and firmware specific binaries rather than a single generic path. Build first, look at what actually landed on the ISO, then correct the menu. Do not let a memtest entry block a release.
On destructive tools, one design note that generalizes past this project. A rescue image contains things that destroy data, and the safety belongs in the launcher rather than in the user’s attention. The pattern worth copying: exclude the boot device from any target list so the tool cannot offer you the USB you booted from, refuse to touch a device that is mounted or has active swap or has device-mapper or RAID holders, and confirm by making the user type the drive’s serial number rather than pressing y. Typing a serial forces you to look at the physical drive. Pressing y is muscle memory, and muscle memory is how the wrong disk gets wiped.
Testing, Which Is Most of the Work
Here is the honest part. Getting a first ISO to build is an evening. Getting one you’d trust on someone’s failing array is months, and essentially all of that time is the loop: build, boot, check, find something, fix, rebuild. This is the step that live-build tutorials skip, and skipping it is why so many custom images are one person’s untested afternoon.
QEMU first, both firmware paths. Never test only one, because the failure modes are completely different and a bootloader problem in one is invisible from the other.
qemu-system-x86_64 -m 4096 -cdrom live-image-amd64.hybrid.iso -enable-kvm -L /usr/share/seabios/
qemu-system-x86_64 -m 4096 -cdrom live-image-amd64.hybrid.iso -enable-kvm -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd
Two classes of smoke test, and the distinction matters. Some checks are safe inside the build chroot: does this command exist, does the binary have all its libraries, is the manifest present, does a config file parse. Those can run before you ever boot, which makes them cheap and means you catch a broken package immediately after the build that introduced it. Other checks are meaningless outside a booted system: can a kernel module load, are the right services running and the wrong ones not, does the GUI actually come up, does networking work. Keep them in separate scripts, run the chroot-safe one from inside the live environment too, and make the live one fail if the chroot one fails.
Ship both scripts inside the image. You want to hand someone an ISO and have them run the validation themselves.
Then real hardware, because QEMU lies by omission. Virtual machines have obliging virtual hardware. They will not surface the WiFi chipset with no firmware, the laptop that won’t come up without nomodeset, the machine whose UEFI disagrees with your boot entries, or the SSH authentication default that only matters when you’re connecting from another physical machine. QEMU tells you the image boots. Real hardware tells you it works.
Keep a checklist per phase and record the odd results, because a surprising amount of testing effort goes into distinguishing “broken” from “behaves unexpectedly.” Real examples from this build, all harmless and all initially confusing: gdisk has no --version flag and prints its version on any input, mkfs.btrfs uses -V rather than --version, foremost and scalpel have no version flag at all so presence has to be confirmed with -h, ipmitool reports “no open device” on any machine without a BMC, and vmware-toolbox-cmd only does anything inside a VMware guest. Write these down the first time. Otherwise you rediscover each one on every rebuild.
One real bug from the same checklists, as an example of what the process is for: on first boot, several tools reported “command not found” despite being installed, which turned out to be a PATH problem in the live environment and was fixed with an /etc/environment entry plus a profile.d drop-in. That is not an interesting bug. It is completely invisible until you boot the thing and type a command, and no amount of careful configuration review would have surfaced it.
How AI can help
The smoke test scripts are the highest-value handoff in this whole project, because they're tedious to write, easy to write badly, and you'll run them hundreds of times. Give an assistant your package lists and ask for a validation script that checks each tool is present and callable, with the correct invocation per tool, and have it flag which tools have no version flag so those get presence checks instead. That single request captures a category of knowledge you would otherwise accumulate painfully across many rebuilds. Ask for the chroot-safe and live-only split explicitly, and for a summary line with a pass and fail count at the end, since scanning a hundred lines of output for one failure is how failures get missed.
Where This Build Stopped
The state as of this writing, stated plainly.
Built and validated. The current ISO is the 2026-05-10 build: 1.6 GB, 1,368 packages, kernel 6.12.86+deb13, and it boots on both BIOS and UEFI. FIO 3.42 is present, so the custom .deb path works end to end. zfs-dkms 2.3.2 is installed against matching headers. Phases 1, 2A, 2B, and 2E have completed their checklists, with both smoke test scripts reporting zero failures. The image is real and it is bootable.
Worth noting the size: it came in at 1.6 GB against a 2.0 to 2.5 GB estimate, so the cleanup hook and Debian’s compression did better than expected.
Not finished. The Phase 2C and 2D checklists have not been formally walked, though the tools they cover are in the image and the smoke tests pass. The guided launchers are version 0.1-alpha and their test checklist has never been executed. Three packages those launchers depend on (sedutil, gsmartcontrol, hardinfo2) are not in the current image, and sedutil in particular isn’t in Debian’s repositories at all, so it needs a source build like FIO did. The SSH password-authentication fix is deployed but needs a retest on a fresh build. Distribution is deliberately unstarted: the intent is to publish a downloadable ISO, with a source repository as an optional later step rather than a commitment.
Why publish the method now. Because what remains is iteration, not discovery. The destination is known and specified in detail; getting there is building, booting, testing, fixing, and rebuilding until the checklist is clean. That loop is worth doing and no longer worth writing about, whereas the structure that makes the loop tractable is the part someone else can use today.
What this guide deliberately left out:
- The erase engine in detail. The secure-erase launcher spans nine methods across ATA, NVMe, SCSI/SAS, eMMC, and TCG Opal, and choosing correctly means decoding capability bits from the drive itself rather than guessing from the interface. That is a much deeper subject than live-build and it deserves its own guide, once the launchers have been tested rather than while they’re alpha.
- Persistence. Deliberately out of scope for v1.
- Repositories, CI, and reproducible-build attestation. Real topics for a project that wants contributors. This one wants a working USB stick first.
What You Spent
Nothing but time, which is the entire argument.
- live-build, Debian, and every tool in the image: $0
- Build host: any machine that can run
debootstrapand QEMU, with enough disk for a chroot and a few ISO builds - Time: an evening to a first bootable image, and considerably longer to one you trust
For comparison, Parted Magic runs about $17 for a single non-expiring version, $59 annually, or $199 for a lifetime license, and it is a genuinely good product that has earned it. SystemRescue is free and excellent if its tool selection and command-line orientation match how you work.
The honest case for building your own is not that it’s cheaper. It’s that a rescue image assembled from the tools you personally reach for, with launchers for the operations you personally get nervous about, fits your hand in a way a general-purpose image cannot. You also learn precisely what is on the stick, which matters the first time you boot it on hardware that is already in trouble. The case against is equally honest: this is a real project, most of it is testing, and if a $17 license and someone else’s testing gets you a working USB stick this afternoon, that is a perfectly rational purchase.
Toolkit Reference
The components that appear across this guide, and the concrete spots where an AI assistant earns its keep.
Build Stack
- live-build
- Debian's native live-image builder. A declarative config tree compiles to a hybrid ISO. The reproducibility is the reason to prefer it over remastering.
- Debian 13 (Trixie)
- The base. Stable matters here specifically because kernel and headers stay paired, which is what DKMS modules need.
- QEMU + OVMF
- Boot-test every build in both BIOS and UEFI. OVMF provides the UEFI firmware. Necessary but not sufficient; real hardware finds different bugs.
- debootstrap
- Builds a clean chroot of the target release. Where custom
.debpackages must be compiled so they don't link against your workstation's libraries. - OpenZFS via DKMS
- The out-of-tree module example. Needs the full chain named explicitly, and Secure Boot blocks the unsigned result.
- FIO
- The newer-than-stable example. Built once as a
.deb, verified withldd, dropped intopackages.chroot/. - Cubic
- The friendlier alternative if you want a one-off image and don't need reproducibility. Worth knowing it exists before committing to live-build.
Where AI Earns Its Keep
- Package list curation
- Generates broad category lists including tools you'd forget, and confidently names some that don't exist in your target release. Pair it with a preflight resolve check and the combination works well.
- The custom .deb build script
- Mechanical with real gotchas. Ask for the
debootstrapchroot build, thelddgate before packaging, and a loud failure if it's run on the workstation instead. - Smoke test generation
- The highest-value handoff here. From your package lists, a validation script with the correct invocation per tool, presence checks for tools with no version flag, and a pass/fail summary line.
- DKMS and Secure Boot diagnosis
- Good at explaining why a module failed to load when the error text doesn't mention the real cause, which is most of the time.
- Destructive-operation safety review
- Ask what happens if a device disappears mid-operation, if two drives share a model string, or if the user pastes the wrong serial. Adversarial review is worth more than more features on anything that erases disks.