rdebootstrap
rdebootstrap is a manifest-driven Debian/Ubuntu bootstrapper written in Rust.
It resolves packages from user-defined APT archives, locks the full dependency
graph, stages arbitrary artifacts, and builds a root filesystem tree inside a
sandbox so maintainer scripts run in a controlled environment. The same engine
is exposed as the debrepo library for embedding in other tooling.
Highlights
- Declarative input –
Manifest.tomllists archives, specs, staged files, local.debs, and metadata whileManifest.<arch>.lockcaptures the fully resolved set for reproducible builds. - Deterministic resolution – Release and Packages files are fetched with GPG verification, optional snapshot pinning, and a solver that locks each spec before anything is installed.
- Sandboxed builds –
buildexpands packages inside an isolated helper namespace; run as root for production ownership or unprivileged while iterating. - Rich spec tooling – add/drop requirements and constraints per spec, stage local files or HTTP artifacts, and include local packages that ship alongside the manifest.
- Fast, resumable downloads – concurrent fetcher (
-n/--downloads) backed by a shared cache and optional transport relaxations for air-gapped or test environments.
Requirements
- Linux host with user namespaces enabled (required by the sandbox helper).
- Rust toolchain ≥ 1.89 (
rustup toolchain install 1.89.0). - System packages needed by
cargoplusgpgme/libgpg-errorfor Release verification. - Optional
sudowhen you need ownership preserved inside the target rootfs.
Installation
# clone this repo
# or install into ~/.cargo/bin
# nix users
The resulting binary lives at target/release/rdebootstrap (or in
~/.cargo/bin when installed).
Typical Workflow
- Create a manifest
rdebootstrap init debian --package ca-certificates --package vim - Iterate on specs
rdebootstrap include --spec desktop openssh-server network-manager
rdebootstrap exclude --spec desktop 'systemd-hwe (= 255.5-1)' - Update and lock
rdebootstrap update --snapshot 20241007T030925Z(or--snapshot now)
This downloads Release/Packages data, solves the specs, and writesManifest.<arch>.lock. - Build a filesystem tree
sudo rdebootstrap build --spec desktop --path ./out
The resulting tree may be used directly with podman (requires the full path because podman enters its own mount namespace):podman run --rm -it --systemd=always --rootfs "$(pwd)/out" bash -l
build unpacks packages into the target directory, stages artifacts, and runs
maintainer scripts in the sandbox so the host stays clean.
Manifest Layout
Manifest.toml sits at the project root unless --manifest <path> is supplied.
The lock file is written next to the manifest by default; use --lock <path> to
override the base path. Lock path rules (for a manifest named <name>.toml):
- no
--lock:<name>.<arch>.lock --lock <dir>/:<dir>/<name>.<arch>.lock--lock <file>.lock:<file>.<arch>.lock(extension replaced)
A small example:
[[]]
= "https://ftp.debian.org/debian/"
= ["trixie"]
= ["main"]
= "https://snapshot.debian.org/archive/debian/@SNAPSHOTID@/"
[]
= ["ca-certificates", "openssh-server"]
= ["README.md"]
[[]]
= "target/debian/mytool_0.1.0_amd64.deb"
= "sha256-..."
Key sections:
[[archive]]— APT repositories with suites, components, optional snapshot templates, trusted keys, and priorities.[[local]]— Local.debfiles copied into the cache and treated like repo packages.[artifact."<name>"]— Files or URLs to drop into the tree during staging.[spec]and[spec.<name>]— Package requirements/constraints, staged artifacts, build-time environment/script, and metadata per spec. Specs can inherit from each other viaextends.
rdebootstrap update keeps the lock file aligned with the manifest, and build
refuses to run if the lock is missing or stale.
Cache and Fetching
- By default caching is enabled and lives in
XDG_CACHE_HOME/rdebootstrapor~/.cache/rdebootstrapifXDG_CACHE_HOMEis unset. - Use
--cache-dir <dir>to point elsewhere or--no-cacheto disable it entirely. - Local artifacts are hashed relative to the manifest directory, so keeping manifests and artifacts in the same repository ensures stable paths.
- Content integrity is enforced via the hashes recorded in the lock file; disabling cache does not bypass verification.
Artifacts and Staging
Artifacts are declared at the top level as [artifact."<name>"] and referenced
from specs via stage = ["<name>", ...]. Use rdebootstrap add artifact to
define them and rdebootstrap stage to attach them to specs.
- Artifact
typeis one of:file,tar,dir,text. - Hashes are serialized in SRI form:
<algo>-<base64>(for exampleblake3-...,sha256-...). - When
rdebootstrapcomputes an artifact hash (for example viaadd artifact), it usesblake3. TARGET_PATHis treated as an absolute path inside the target filesystem (non-absolute values are auto-prefixed with/during staging).{file|text}.ext /path/target→/path/target{file|text}.ext /path/target/→/path/target/file.ext
file.tar /path/target(/?)→ extracted under/path/targetdir /path/target(/?)→ copied under/path/target- Filename resolution for
{file|text}artifacts happens during staging; manifests keep the rawtargetvalue. - Auto-unpack: tar archives and compressed files (
.gz,.xz,.bz2,.zst,.zstd) are unpacked by default; use--no-unpackto keep them as-is. - Safety: tar unpacking rejects absolute paths,
..traversal, and special entries like device nodes. - Inline text artifacts (
type = "text") embed atextvalue in the manifest and write it totargetduring staging.rdebootstrap add artifact @filecreates a text artifact from a UTF-8 file (target path required).
Build Environment and Scripts
Specs can set:
build-env— key/value environment variables applied to bothdpkg --configureandbuild-script.build-script— a bash script executed after package configuration. Scripts fromextendsare executed in order (base → derived).
Use rdebootstrap edit env / rdebootstrap edit script to edit these fields.
rdebootstrap build supports --executor sandbox (default) and
--executor podman. The executor matters mainly for rootless runs: sandbox
uses the built-in helper, while podman runs configuration inside
podman run --rootfs ... (which may require a working rootless podman
environment such as a valid XDG runtime directory).
CLI Tour
init– bootstrap a manifest from vendor presets (debian,ubuntu,devuan) or explicit archives.edit– edit the manifest (rdebootstrap edit) or spec metadata (edit env,edit script).add archive,add local– append repositories or register a local.deb.include/exclude– add requirements or version constraints to a spec.drop– remove requirements or constraints.stage/unstage– add or remove artifacts (local files or URLs).update– refresh metadata, solve dependencies, and rewrite the lock file (supports--snapshot,--localsrefreshes local packages and local artifacts).list,search,show– inspect the resolved package universe.build– expand a spec into a directory, running maintainer scripts within the sandbox helper.
Authentication
-a/--authselects the auth source: omit for optionalauth.tomlnext to the manifest, usefile:/path/to/auth.toml(or just a path), orvault:<mount>/<path>to read secrets from Vault.
Do not commit auth.toml to version control.
- Auth file (
auth.toml) supports per-host entries:
[[]]
= "deb.example.com"
= "user"
= "inline" # or password.env / password.cmd
[[]]
= "deb.other.com"
= "token-string"
[[]]
= "deb.tls.com"
= "relative/cert.pem" # relative paths are resolved from the auth file directory
= "relative/key.pem"
# password/env/cmd/file are also supported for passwords
password.env reads an env var, password.cmd runs a shell command (cwd = auth
file dir), and password.file/password.path load file content. Tokens and
cert/key accept the same source forms.
- Vault secrets: pass
--auth vault:<mount>/<path>(for examplevault:secret/data/repos). Each host lives at<mount>/<path>/<host>and contains JSON like:
VAULT_ADDR, VAULT_TOKEN, VAULT_CACERT, and VAULT_SKIP_VERIFY influence
the Vault client.
Global flags of note:
--manifest <path>selects an alternate manifest.-l/--lock <path>overrides the lock file base path (end with/to treat it as a directory).--arch <arch>switches the target architecture (default: host arch).-n/--downloads <N>controls concurrent downloads (default: 20).--cache-dir/--no-cacheadjust caching.-k/--insecuredisables TLS certificate and hostname verification (not recommended).
Verification controls (scoped):
--no-verify(oninit,add archive,update) skips InRelease signature verification (not recommended).-K/--allow-insecure(on archive definitions forinitandadd archive, orallow-insecure = truein the manifest) fetchesReleaseinstead ofInRelease.
Run rdebootstrap <command> --help for exhaustive usage information.
Known Rough Edges
- Staging/unpacking happens concurrently; this makes
rdebootstrapincompatible withdpkg-divertworkflows. -q/--quietand-d/--debugcurrently affect onlyrdebootstrapoutput, not the output ofdpkg --configureorbuild-script.
Development
cargo fmt,cargo clippy --all-targets, andcargo testkeep the codebase healthy.cargo bench -p debrepo version(and other benches underbenches/) run Criterion benchmarks.- The crate can also be embedded directly by depending on
debrepoand drivingManifest/HostCachefrom your own host tooling.
License
Licensed under the MIT License.