commit 860ac90f8c820de948e3d2177e9e5ddb00ea02c1 Author: josch Date: Sun Oct 27 11:17:40 2024 +0000 initial version diff --git a/Home.md b/Home.md new file mode 100644 index 0000000..f804973 --- /dev/null +++ b/Home.md @@ -0,0 +1,1466 @@ +# NAME + +mmdebstrap - multi-mirror Debian chroot creation + +# SYNOPSIS + +**mmdebstrap** \[**OPTION...**\] \[_SUITE_ \[_TARGET_ \[_MIRROR_...\]\]\] + +# DESCRIPTION + +**mmdebstrap** creates a Debian chroot of _SUITE_ into _TARGET_ from one or +more _MIRROR_s. It is meant as an alternative to the debootstrap tool (see +section **DEBOOTSTRAP**). In contrast to debootstrap it uses apt to resolve +dependencies and is thus able to use more than one mirror and resolve more +complex dependency relationships. See section **OPERATION** for an overview of +how **mmdebstrap** works internally. + +The _SUITE_ option may either be a valid release code name (eg, sid, bookworm, +trixie) or a symbolic name (eg, unstable, testing, stable, oldstable). Any +suite name that works with apt on the given mirror will work. The _SUITE_ +option is optional if no _TARGET_ and no _MIRROR_ option is provided. If +_SUITE_ is missing, then the information of the desired suite has to come from +standard input as part of a valid apt sources.list file or be set up via hooks. +The value of the _SUITE_ argument will be used to determine which apt index to +use for finding out the set of `Essential:yes` packages and/or the set of +packages with the right priority for the selected variant. This functionality +can be disabled by choosing the empty string for _SUITE_. See the section +**VARIANTS** for more information. + +The _TARGET_ option may either be the path to a directory, the path to a +tarball filename, the path to a squashfs image, the path to an ext2 or ext4 +image, a FIFO, a character special device, or `-`. The _TARGET_ option is +optional if no _MIRROR_ option is provided. If _TARGET_ is missing or if +_TARGET_ is `-`, an uncompressed tarball will be sent to standard output. +Without the **--format** option, _TARGET_ will be used to choose the format. +See the section **FORMATS** for more information. + +The _MIRROR_ option may either be provided as a URI, in apt one-line format, +as a path to a file in apt's one-line or deb822-format, or `-`. If no +_MIRROR_ option is provided, then [http://deb.debian.org/debian](http://deb.debian.org/debian) is used as +the default. If _SUITE_ does not refer to "unstable" or "testing", then +_SUITE_-updates and _SUITE_-security mirrors are automatically added. If a +_MIRROR_ option starts with "deb " or "deb-src " then it is used as a one-line +format entry for apt's sources.list inside the chroot. If a _MIRROR_ option +contains a "://" then it is interpreted as a mirror URI and the apt line inside +the chroot is assembled as "deb \[arch=A\] B C D" where A is the host's native +architecture, B is the _MIRROR_, C is the given _SUITE_ and D is the +components given via **--components** (defaults to "main"). If a _MIRROR_ +option happens to be an existing file, then its contents are written into the +chroot's sources.list (if the first _MIRROR_ is a file in one-line format) or +into the chroot's sources.list.d directory, named with the extension .list or +.sources, depending on whether the file is in one-line or deb822 format, +respectively. If _MIRROR_ is `-` then standard input is pasted into the +chroot's sources.list. More than one mirror can be specified and are appended +to the chroot's sources.list in the given order. If you specify a https or tor +_MIRROR_ and you want the chroot to be able to update itself, don't forget to +also install the ca-certificates package, the apt-transport-https package for +apt versions less than 1.5 and/or the apt-transport-tor package using the +**--include** option, as necessary. + +All status output is printed to standard error unless **--logfile** is used to +redirect it to a file or **--quiet** or **--silent** is used to suppress any +output on standard error. Help and version information will be printed to +standard error with the **--help** and **--version** options, respectively. +Otherwise, an uncompressed tarball might be sent to standard output if +_TARGET_ is `-` or if no _TARGET_ was specified. + +# OPTIONS + +Options are case insensitive. Short options may be bundled. Long options +require a double dash and may be abbreviated to uniqueness. Options can be +placed anywhere on the command line, even before or mixed with the _SUITE_, +_TARGET_, and _MIRROR_ arguments. A double dash `--` can be used to stop +interpreting command line arguments as options to allow _SUITE_, _TARGET_ and +_MIRROR_ arguments that start with a single or double dash. Option order only +matters for options that can be passed multiple times as documented below. + +- **-h,--help** + + Print synopsis and options of this man page and exit. + +- **--man** + + Show the full man page as generated from Perl POD in a pager. This requires + the perldoc program from the perl-doc package. This is the same as running: + + pod2man /usr/bin/mmdebstrap | man -l - + +- **--version** + + Print the **mmdebstrap** version and exit. + +- **--variant**=_name_ + + Choose which package set to install. Valid variant _name_s are **extract**, + **custom**, **essential**, **apt**, **required**, **minbase**, **buildd**, + **important**, **debootstrap**, **-**, and **standard**. The default variant is + **debootstrap**. See the section **VARIANTS** for more information. + +- **--mode**=_name_ + + Choose how to perform the chroot operation and create a filesystem with + ownership information different from the current user. Valid mode _name_s are + **auto**, **sudo**, **root**, **unshare**, **fakeroot**, **fakechroot** and + **chrootless**. The default mode is **auto**. See the section **MODES** for more + information. + +- **--format**=_name_ + + Choose the output format. Valid format _name_s are **auto**, **directory**, + **tar**, **squashfs**, **ext2**, **ext4** and **null**. The default format is + **auto**. See the section **FORMATS** for more information. + +- **--aptopt**=_option_|_file_ + + Pass arbitrary _option_s to apt. Will be permamently added to + `/etc/apt/apt.conf.d/99mmdebstrap` inside the chroot. Use hooks for temporary + configuration options. Can be specified multiple times. Each _option_ will be + appended to 99mmdebstrap. A semicolon will be added at the end of the option if + necessary. If the command line argument is an existing _file_, the content of + the file will be appended to 99mmdebstrap verbatim. + + Example: This is necessary for allowing old timestamps from snapshot.debian.org + + --aptopt='Acquire::Check-Valid-Until "false"' + --aptopt='Apt::Key::gpgvcommand "/usr/libexec/mmdebstrap/gpgvnoexpkeysig"' + + Example: Settings controlling download of package description translations + + --aptopt='Acquire::Languages { "environment"; "en"; }' + --aptopt='Acquire::Languages "none"' + + Example: Enable installing Recommends (by default **mmdebstrap** doesn't) + + --aptopt='Apt::Install-Recommends "true"' + + Example: Configure apt-cacher or apt-cacher-ng as an apt proxy + + --aptopt='Acquire::http { Proxy "http://127.0.0.1:3142"; }' + + Example: For situations in which the apt sandbox user cannot access the chroot + + --aptopt='APT::Sandbox::User "root"' + + Example: Minimizing the number of packages installed from experimental + + --aptopt='APT::Solver "aspcud"' + --aptopt='APT::Solver::aspcud::Preferences + "-count(solution,APT-Release:=/a=experimental/),-removed,-changed,-new"' + +- **--keyring**=_file_|_directory_ + + Change the default keyring to use by apt during the initial setup. This is + similar to setting **Dir::Etc::Trusted** and **Dir::Etc::TrustedParts** using + **--aptopt** except that the latter setting will be permanently stored in the + chroot while the keyrings passed via **--keyring** will only be visible to apt + as run by **mmdebstrap**. Do not use **--keyring** if apt inside the chroot needs + to know about your keys after the initial chroot creation by **mmdebstrap**. + This option is mainly intended for users who use **mmdebstrap** as a + **deboostrap** drop-in replacement. As such, it is probably not what you want to + use if you use **mmdebstrap** with more than a single mirror unless you pass it + a directory containing all the keyrings you need. + + By default, the local setting of **Dir::Etc::Trusted** and + **Dir::Etc::TrustedParts** are used to choose the keyring used by apt as run by + **mmdebstrap**. These two locations are set to `/etc/apt/trusted.gpg` and + `/etc/apt/trusted.gpg.d` by default. Depending on whether a file or directory + is passed to this option, the former and latter default can be changed, + respectively. Since apt only supports a single keyring file and directory, + respectively, you can **not** use this option to pass multiple files and/or + directories. Using the `--keyring` argument in the following way is equal to + keeping the default: + + --keyring=/etc/apt/trusted.gpg --keyring=/etc/apt/trusted.gpg.d + + If you need to pass multiple keyrings, use the `signed-by` option when + specifying the mirror like this: + + mmdebstrap mysuite out.tar "deb [signed-by=/path/to/key.gpg] http://..." + + Another reason to use `signed-by` instead of **--keyring** is if apt inside the + chroot needs to know by what key the repository is signed even after the + initial chroot creation. + + The `signed-by` option will automatically be added to the final + `sources.list` if the keyring required for the selected _SUITE_ is not yet + trusted by apt. Automatically adding the `signed-by` option in these cases + requires `gpg` to be installed. If `gpg` and `ubuntu-archive-keyring` are + installed, then you can create a Ubuntu Bionic chroot on Debian like this: + + mmdebstrap bionic ubuntu-bionic.tar + + The resulting chroot will have a `source.list` with a `signed-by` option + pointing to `/usr/share/keyrings/ubuntu-archive-keyring.gpg`. + + You do not need to use **--keyring** or `signed-by` if you placed the keys that + apt needs to know about into `/etc/apt/trusted.gpg.d` in the **--setup-hook** + (which is before `apt update` runs), for example by using the **copy-in** + special hook. You also need to copy your keys into the chroot explicitly if the + key you passed via `signed-by` points to a location that is not otherwise + populated during chroot creation (for example by installing a keyring package). + +- **--dpkgopt**=_option_|_file_ + + Pass arbitrary _option_s to dpkg. Will be permanently added to + `/etc/dpkg/dpkg.cfg.d/99mmdebstrap` inside the chroot. Use hooks for temporary + configuration options. Can be specified multiple times. Each _option_ will be + appended to 99mmdebstrap. If the command line argument is an existing _file_, + the content of the file will be appended to 99mmdebstrap verbatim. + + Example: Exclude paths to reduce chroot size + + --dpkgopt='path-exclude=/usr/share/man/*' + --dpkgopt='path-include=/usr/share/man/man[1-9]/*' + --dpkgopt='path-exclude=/usr/share/locale/*' + --dpkgopt='path-include=/usr/share/locale/locale.alias' + --dpkgopt='path-exclude=/usr/share/doc/*' + --dpkgopt='path-include=/usr/share/doc/*/copyright' + --dpkgopt='path-include=/usr/share/doc/*/changelog.Debian.*' + +- **--include**=_pkg1_\[,_pkg2_,...\] + + Comma or whitespace separated list of packages which will be installed in + addition to the packages installed by the specified variant. The direct and + indirect hard dependencies will also be installed. The behaviour of this + option depends on the selected variant. The **extract** and **custom** variants + install no packages by default, so for these variants, the packages specified + by this option will be the only ones that get either extracted or installed by + dpkg, respectively. For all other variants, apt is used to install the + additional packages. Package names are directly passed to apt and thus, you + can use apt features like `pkg/suite`, `pkg=version`, `pkg-`, use a glob or + regex for `pkg`, use apt patterns or pass a path to a .deb package file (see + below for notes concerning passing the path to a .deb package file in + **unshare** mode). See [apt(8)](http://man.he.net/man8/apt) for the supported syntax. + + The option can be specified multiple times and the packages are concatenated in + the order in which they are given on the command line. If later list items are + repeated, then they get dropped so that the resulting package list is free of + duplicates. So the following are equivalent: + + --include="pkg1/stable pkg2=1.0 pkg3-" + --include=pkg1/stable,pkg2=1.0,pkg3-,,, + --incl=pkg1/stable --incl="pkg2=1.0 pkg3-" --incl=pkg2=1.0,pkg3- + + Since the list of packages is separated by comma or whitespace, it is not + possible to mix apt patterns or .deb package file paths containing either + commas or whitespace with normal package names. If you do, your patterns and + paths will be split by comma and whitespace as well and become useless. To pass + such a pattern or package file path, put them into their own **--include** + option. If the argument to **--include** starts with an apt pattern or with a + file path, then it will not be split: + + --include="?or(?priority(required), ?priority(important))" + --include="./path/to/deb with spaces/and,commas/foo.deb" + + Specifically, all arguments to **--include** that start with a `?`, `!`, `~`, + `(`, `/`, `./` or `../` are not split and treated as single arguments to + apt. To add more packages, use multiple **--include** options. To disable this + detection of patterns and paths, start the argument to **--include** with a + comma or whitespace. + + If you pass the path to a .deb package file using **--include**, **mmdebstrap** + will ensure that the path exists. If the path is a relative path, it will + internally by converted to an absolute path. Since apt (outside the chroot) + passes paths to dpkg (on the inside) verbatim, you have to make the .deb + package available under the same path inside the chroot as well or otherwise + dpkg inside the chroot will be unable to access it. This can be achieved using + a setup-hook. A hook that automatically makes the contents of `file://` + mirrors as well as .deb packages given with **--include** available inside the + chroot is provided by **mmdebstrap** as + **--hook-dir=/usr/share/mmdebstrap/hooks/file-mirror-automount**. This hook + takes care of copying all relevant file to their correct locations and cleans + up those files at the end. In **unshare** mode, the .deb package paths have to + be accessible by the unshared user as well. This means that the package itself + likely must be made world-readable and all directory components on the path to + it world-executable. + +- **--components**=_comp1_\[,_comp2_,...\] + + Comma or whitespace separated list of components like main, contrib, non-free + and non-free-firmware which will be used for all URI-only _MIRROR_ arguments. + The option can be specified multiple times and the components are concatenated + in the order in which they are given on the command line. If later list items + are repeated, then they get dropped so that the resulting component list is + free of duplicates. So the following are equivalent: + + --components="main contrib non-free non-free-firmware" + --components=main,contrib,non-free,non-free-firmware + --comp=main --comp="contrib non-free" --comp="main,non-free-firmware" + +- **--architectures**=_native_\[,_foreign1_,...\] + + Comma or whitespace separated list of architectures. The first architecture is + the _native_ architecture inside the chroot. The remaining architectures will + be added to the foreign dpkg architectures. Without this option, the _native_ + architecture of the chroot defaults to the native architecture of the system + running **mmdebstrap**. The option can be specified multiple times and values + are concatenated. If later list items are repeated, then they get dropped so + that the resulting list is free of duplicates. So the following are + equivalent: + + --architectures="amd64 armhf mipsel" + --architectures=amd64,armhf,mipsel + --arch=amd64 --arch="armhf mipsel" --arch=armhf,mipsel + +- **--simulate**, **--dry-run** + + Run apt-get with **--simulate**. Only the package cache is initialized but no + binary packages are downloaded or installed. Use this option to quickly check + whether a package selection within a certain suite and variant can in principle + be installed as far as their dependencies go. If the output is a tarball, then + no output is produced. If the output is a directory, then the directory will be + left populated with the skeleton files and directories necessary for apt to run + in it. No hooks are executed in with **--simulate** or **--dry-run**. + +- **--setup-hook**=_command_ + + Execute arbitrary _command_s right after initial setup (directory creation, + configuration of apt and dpkg, ...) but before any packages are downloaded or + installed. At that point, the chroot directory does not contain any + executables and thus cannot be chroot-ed into. See section **HOOKS** for more + information. + + Example: add additional apt sources entries on top of the default ones: + + --setup-hook='echo "deb http..." > "$1"/etc/apt/sources.list.d/custom.list' + + Example: Setup chroot for installing a sub-essential busybox-based chroot + with --variant=custom + \--include=dpkg,busybox,libc-bin,base-files,base-passwd,debianutils + + --setup-hook='mkdir -p "$1/bin"' + --setup-hook='for p in awk cat chmod chown cp diff echo env grep less ln + mkdir mount rm rmdir sed sh sleep sort touch uname mktemp; do + ln -s busybox "$1/bin/$p"; done' + --setup-hook='echo root:x:0:0:root:/root:/bin/sh > "$1/etc/passwd"' + --setup-hook='printf "root:x:0:\nmail:x:8:\nutmp:x:43:\n" > "$1/etc/group"' + + For a more elegant way for setting up a sub-essential busybox-based chroot, see + the **--hook-dir** option below. + +- **--extract-hook**=_command_ + + Execute arbitrary _command_s after the Essential:yes packages have been + extracted but before installing them. See section **HOOKS** for more + information. + + Example: Install busybox symlinks + + --extract-hook='chroot "$1" /bin/busybox --install -s' + +- **--essential-hook**=_command_ + + Execute arbitrary _command_s after the Essential:yes packages have been + installed but before installing the remaining packages. The hook is not + executed for the **extract** and **custom** variants. See section **HOOKS** for + more information. + + Example: Enable unattended upgrades + + --essential-hook='echo unattended-upgrades + unattended-upgrades/enable_auto_updates boolean true + | chroot "$1" debconf-set-selections' + + Example: Select Europe/Berlin as the timezone + + --essential-hook='echo tzdata tzdata/Areas select Europe + | chroot "$1" debconf-set-selections' + --essential-hook='echo tzdata tzdata/Zones/Europe select Berlin + | chroot "$1" debconf-set-selections' + +- **--customize-hook**=_command_ + + Execute arbitrary _command_s after the chroot is set up and all packages got + installed but before final cleanup actions are carried out. See section + **HOOKS** for more information. + + Example: Add a user without a password + + --customize-hook='chroot "$1" useradd --home-dir /home/user + --create-home user' + --customize-hook='chroot "$1" passwd --delete user' + + Example: set up `/etc/hostname` and `/etc/hosts` + + --customize-hook='echo host > "$1/etc/hostname"' + --customize-hook='echo "127.0.0.1 localhost host" > "$1/etc/hosts"' + + Example: to mimic **debootstrap** behaviour, **mmdebstrap** copies from the host. + Remove them in a **--customize-hook** to make the chroot reproducible across + multiple hosts: + + --customize-hook='rm "$1"/etc/resolv.conf' + --customize-hook='rm "$1"/etc/hostname' + +- **--hook-directory**=_directory_ + + Execute scripts in _directory_ with filenames starting with `setup`, + `extract`, `essential` or `customize`, at the respective stages during an + mmdebstrap run. The files must be marked executable. Their extension is + ignored. Subdirectories are not traversed. This option is a short-hand for + specifying the remaining four hook options individually for each file in the + directory. If there are more than one script for a stage, then they are added + alphabetically. This is useful in cases, where a user wants to run the same + hooks frequently. For example, given a directory `./hooks` with two scripts + `setup01-foo.sh` and `setup02-bar.sh`, this call: + + mmdebstrap --customize=./scriptA --hook-dir=./hooks --setup=./scriptB + + is equivalent to this call: + + mmdebstrap --customize=./scriptA --setup=./hooks/setup01-foo.sh \ + --setup=./hooks/setup02-bar.sh --setup=./scriptB + + The option can be specified multiple times and scripts are added to the + respective hooks in the order the options are given on the command line. Thus, + if the scripts in two directories depend upon each other, the scripts must be + placed into a common directory and be named such that they get added in the + correct order. + + Example 1: Run mmdebstrap with eatmydata + + --hook-dir=/usr/share/mmdebstrap/hooks/eatmydata + + Example 2: Setup chroot for installing a sub-essential busybox-based chroot + + --hook-dir=/usr/share/mmdebstrap/hooks/busybox + + Example 3: Automatically mount all directories referenced by `file://` mirrors + into the chroot + + --hook-dir=/usr/share/mmdebstrap/hooks/file-mirror-automount + +- **--skip**=_stage_\[,_stage_,...\] + + **mmdebstrap** tries hard to implement sensible defaults and will try to stop + you before shooting yourself in the foot. This option is for when you are sure + you know what you are doing and allows one to skip certain actions and safety + checks. See section **OPERATION** for a list of possible arguments and their + context. The option can be specified multiple times or you can separate + multiple values by comma or whitespace. + +- **-q,--quiet**, **-s,--silent** + + Do not write anything to standard error. If used together with **--verbose** or + **--debug**, only the last option will take effect. + +- **-v,--verbose** + + Instead of progress bars, write the dpkg and apt output directly to standard + error. If used together with **--quiet** or **--debug**, only the last option + will take effect. + +- **-d,--debug** + + In addition to the output produced by **--verbose**, write detailed debugging + information to standard error. Errors will print a backtrace. If used together + with **--quiet** or **--verbose**, only the last option will take effect. + +- **--logfile**=_filename_ + + Instead of writing status information to standard error, write it into the + file given by _filename_. + +# MODES + +Creating a Debian chroot requires not only permissions for running chroot but +also the ability to create files owned by the superuser. The selected mode +decides which way this is achieved. + +- **auto** + + This mode automatically selects a fitting mode. If the effective user id is the + one of the superuser, then the **sudo** mode is chosen. Otherwise, the + **unshare** mode is picked if `/etc/subuid` and `/etc/subgid` are set up + correctly. Should that not be the case and if the fakechroot binary exists, the + **fakechroot** mode is chosen. + +- **sudo**, **root** + + This mode directly executes chroot and is the same mode of operation as is + used by debootstrap. It is the only mode that can directly create a directory + chroot with the right permissions. If the chroot directory is not accessible + by the \_apt user, then apt sandboxing will be automatically disabled. This mode + needs to be able to mount and thus requires `CAP_SYS_ADMIN`. + +- **unshare** + + When used as a normal (not root) user, this mode uses Linux user namespaces to + allow unprivileged use of chroot and creation of files that appear to be owned + by the superuser inside the unshared namespace. A tarball created in this mode + will be bit-by-bit identical to a tarball created with the **root** mode. With + this mode, the only binaries that will run as the root user will be + [newuidmap(1)](http://man.he.net/man1/newuidmap) and [newgidmap(1)](http://man.he.net/man1/newgidmap) via their setuid bit. Running those + successfully requires `/etc/subuid` and `/etc/subgid` to have an entry for + your username. This entry was usually created by [adduser(8)](http://man.he.net/man8/adduser) already. + + The unshared user will not automatically have access to the same files as you + do. This is intentional and an additional security against unintended changes + to your files that could theoretically result from running **mmdebstrap** and + package maintainer scripts. To copy files in and out of the chroot, either use + globally readable or writable directories or use special hooks like **copy-in** + and **copy-out**. + + Besides the user namespace, the mount, pid (process ids), uts (hostname) and + ipc namespaces will be unshared as well. See the man pages of [namespaces(7)](http://man.he.net/man7/namespaces) + and [unshare(2)](http://man.he.net/man2/unshare) as well as the manual pages they are linking to. + + A directory chroot created with this mode will end up with wrong ownership + information (seen from outside the unshared user namespace). For correct + ownership information, the directory must be accessed from a user namespace + with the right subuid/subgid offset, like so: + + $ lxc-usernsexec -- lxc-unshare -s 'MOUNT|PID|UTSNAME|IPC' -- \ + > /usr/sbin/chroot ./debian-rootfs /bin/bash + + Or without LXC: + + $ mmdebstrap --unshare-helper /usr/sbin/chroot ./debian-rootfs /bin/bash + + Or without mmdebstrap: + + $ unshare --map-auto --map-user=65536 --map-group=65536 --keep-caps -- \ + > /usr/sbin/chroot ./debian-rootfs /bin/bash + + The above uses `--map-auto` to map the block of user/group ids for the + effective user/group to a block starting at user/group ID 0. We also want to + map the current effective user/group ID into the subuid/subgid range using + `--map-user` and `--map-group`, respectively. But if that uid/gid overlaps + with the respective range, a "hole" will be removed from the mapping by the + [unshare(1)](http://man.he.net/man1/unshare) utility and the remaining uid/gid values will get shifted. Thus, + we map the current effective user/group ID to the highest possible uid/gid, + putting them at the end. Since that means that the user/group will be "nobody" + and not "root" inside the namespace, `--keep-caps` propagate permitted + capabilities into the ambient set and thus give the user `CAP_DAC_OVERRIDE` + and other capabilities that it would've had. The following does **NOT** work: + + $ unshare --map-root-user --map-auto ... # or equavalient: + $ unshare --map-user=0 --map-group=0 --map-users=auto --map-groups=auto ... + + Using the format of [lxc-usernsexec(1)](http://man.he.net/man1/lxc-usernsexec), these options will result in the + following mapping: + + 0:$UID:1 + 1:$SUBUIDBASE:65535 + + So a hole is punched in the automatically mapped range at the very beginning to + make space for the user id of the current user as root inside the namespace and + the remaining uids are shifted. With `--map-user=65536` the mapping is as + follows and places the current user at the end of the range, preventing any + shifting: + + 0:$SUBUIDBASE:65536 + 65536:$UID:1 + + Lastly, if you don't mind using superuser privileges and have systemd-nspawn + available and you know your subuid/subgid offset (100000 in this example): + + $ sudo systemd-nspawn --private-users=100000 \ + > --directory=./debian-rootfs /bin/bash + + A directory created in **unshare** mode cannot be removed the normal way. + Instead, use something like this: + + $ unshare --map-root-user --map-auto rm -rf ./debian-rootfs + + The above [unshare(1)](http://man.he.net/man1/unshare) command will map user and group ids into different + ranges compared to the mapping used by **mmdebstrap** (effectively shifting them + one up) but it will provide the required capabilities for the removal + operation. + + If this mode is used as the root user, the user namespace is not unshared (but + the mount namespace and other still are) and created directories will have + correct ownership information. This is also useful in cases where the root user + wants the benefits of an unshared mount namespace to prevent accidentally + messing up the system. + +- **fakeroot**, **fakechroot** + + This mode will exec **mmdebstrap** again under `fakechroot fakeroot`. A + directory chroot created with this mode will end up with wrong permissions. If + you need a directory then run **mmdebstrap** under `fakechroot fakeroot -s + fakeroot.env` and use `fakeroot.env` later when entering the chroot with + `fakechroot fakeroot -i fakeroot.env chroot ...`. This mode will not work if + maintainer scripts are unable to handle `LD_PRELOAD` correctly like the + package **initramfs-tools** until version 0.132. This mode will also not work + with a different libc inside the chroot than on the outside. See the section + **LIMITATIONS** in [fakechroot(1)](http://man.he.net/man1/fakechroot). + +- **chrootless** + + Uses the dpkg option `--force-script-chrootless` to install packages into + _TARGET_ without dpkg and apt inside _TARGET_ but using apt and dpkg from the + machine running **mmdebstrap**. Maintainer scripts are run without chrooting + into _TARGET_ and rely on their dependencies being installed on the machine + running **mmdebstrap**. Only very few packages support this mode. Namely, as of + 2022, not all essential packages support it. See + https://wiki.debian.org/Teams/Dpkg/Spec/InstallBootstrap or the + dpkg-root-support usertag of debian-dpkg@lists.debian.org in the Debian bug + tracking system. **WARNING**: if this option is used carelessly with packages + that do not support `DPKG_ROOT`, this mode can result in undesired changes to + the system running **mmdebstrap** because maintainer-scripts will be run without + [chroot(1)](http://man.he.net/man1/chroot). Make sure to run this mode without superuser privileges and/or + inside a throw-away chroot environment like so: + + mmdebstrap --variant=apt --include=mmdebstrap \ + --customize-hook='chroot "$1" mmdebstrap --mode=chrootless + --variant=apt unstable chrootless.tar' \ + --customize-hook='copy-out chrootless.tar .' unstable /dev/null + +# VARIANTS + +All package sets also include the direct and indirect hard dependencies (but +not recommends) of the selected package sets. The variants **minbase**, +**buildd** and **-**, resemble the package sets that debootstrap would install +with the same _--variant_ argument. The release with a name matching the +_SUITE_ argument as well as the native architecture will be used to determine +the `Essential:yes` and priority values. To select packages with matching +priority from any suite, specify the empty string for _SUITE_. The default +variant is **debootstrap**. + +- **extract** + + Installs nothing by default (not even `Essential:yes` packages). Packages + given by the `--include` option are extracted but will not be installed. + +- **custom** + + Installs nothing by default (not even `Essential:yes` packages). Packages + given by the `--include` option will be installed. If another mode than + **chrootless** was selected and dpkg was not part of the included package set, + then this variant will fail because it cannot configure the packages. + +- **essential** + + `Essential:yes` packages. If _SUITE_ is a non-empty string, then only + packages from the archive with suite or codename matching _SUITE_ will be + considered for selection of `Essential:yes` packages. + +- **apt** + + The **essential** set plus apt. + It is roughly equivalent to running mmdebstrap with + + --variant=essential --include="apt" + +- **buildd** + + The **essential** set plus apt and build-essential. + It is roughly equivalent to running mmdebstrap with + + --variant=essential --include="apt,build-essential" + +- **required**, **minbase** + + The **essential** set plus all packages with Priority:required. + It is roughly equivalent to running mmdebstrap with + + --variant=essential --include="?priority(required)" + +- **important**, **debootstrap**, **-** + + The **required** set plus all packages with Priority:important. This is the + default of debootstrap. It is roughly equivalent to running mmdebstrap with + + --variant=essential --include="~prequired|~pimportant" + +- **standard** + + The **important** set plus all packages with Priority:standard. + It is roughly equivalent to running mmdebstrap with + + --variant=essential --include="~prequired|~pimportant|~pstandard" + +# FORMATS + +The output format of **mmdebstrap** is specified using the **--format** option. +Without that option the default format is _auto_. The following formats exist: + +- **auto** + + When selecting this format (the default), the actual format will be inferred + from the _TARGET_ positional argument. If _TARGET_ was not specified, then + the **tar** format will be chosen. If _TARGET_ happens to be `/dev/null` or if + standard output is `/dev/null`, then the **null** format will be chosen. If + _TARGET_ is an existing directory, and does not equal to `-`, then the + **directory** format will be chosen. If _TARGET_ ends with `.tar` or with one + of the filename extensions listed in the section **COMPRESSION**, or if + _TARGET_ equals `-`, or if _TARGET_ is a named pipe (fifo) or if _TARGET_ + is a character special file, then the **tar** format will be chosen. If + _TARGET_ ends with `.squashfs` or `.sqfs`, then the **squashfs** format will + be chosen. If _TARGET_ ends with `.ext2` then the **ext2** format will be + chosen. If _TARGET_ ends with `.ext4` then the **ext4** format will be + chosen. If none of these conditions apply, the **directory** format will be + chosen. + +- **directory**, **dir** + + A chroot directory will be created in _TARGET_. If the directory already + exists, it must either be empty or only contain an empty `lost+found` + directory. The special _TARGET_ `-` does not work with this format because a + directory cannot be written to standard output. If you need your directory be + named `-`, then just explicitly pass the relative path to it like `./-`. If + a directory is chosen as output in any other mode than **sudo**, then its + contents will have wrong ownership information and special device files will be + missing. Refer to the section **MODES** for more information. + +- **tar** + + A temporary chroot directory will be created in `$TMPDIR` or `/tmp` if + `$TMPDIR` is not set. A tarball of that directory will be stored in _TARGET_ + or sent to standard output if _TARGET_ was omitted or if _TARGET_ equals + `-`. If _TARGET_ ends with one of the filename extensions listed in the + section **COMPRESSION**, then a compressed tarball will be created. The tarball + will be in POSIX 1003.1-2001 (pax) format and will contain extended attributes. + To preserve the extended attributes, you have to pass **--xattrs + \--xattrs-include='\*'** to tar when extracting the tarball. + +- **squashfs**, **sqfs** + + A temporary chroot directory will be created in `$TMPDIR` or `/tmp` if + `$TMPDIR` is not set. A tarball of that directory will be piped to the + `tar2sqfs` utility, which will create an xz compressed squashfs image with a + blocksize of 1048576 bytes in _TARGET_. The special _TARGET_ `-` does not + work with this format because `tar2sqfs` can only write to a regular file. If + you need your squashfs image be named `-`, then just explicitly pass the + relative path to it like `./-`. The `tar2sqfs` tool only supports a limited + set of extended attribute prefixes. Therefore, extended attributes are disabled + in the resulting image. If you need them, create a tarball first and remove the + extended attributes from its pax headers. Refer to the **EXAMPLES** section for + how to achieve this. + +- **ext2** + + A temporary chroot directory will be created in `$TMPDIR` or `/tmp` if + `$TMPDIR` is not set. A tarball of that directory will be piped to the + `genext2fs` utility, which will create an ext2 image that will be + approximately 90% full in _TARGET_. The special _TARGET_ `-` does not work + with this format because `genext2fs` can only write to a regular file. If you + need your ext2 image be named `-`, then just explicitly pass the relative path + to it like `./-`. To convert the result to an ext3 image, use `tune2fs -O + has_journal TARGET` and to convert it to ext4, use `tune2fs -O + extents,uninit_bg,dir_index,has_journal TARGET`. + + **CAUTION**: the ext2 format does not support timestamps beyond 2038 January 19, + does not support sub-second precision timestamps and does not support extended + attributes. Its inode size of 128 prevents adding these features with tune2fs + later on. + +- **ext4** + + A temporary chroot directory will be created in `$TMPDIR` or `/tmp` if + `$TMPDIR` is not set. A tarball of that directory will be piped to the + `mke2fs` utility, which will create an ext4 image that will be approximately + 90% full in _TARGET_. The special _TARGET_ `-` does not work with this + format because `mke2fs` can only write to a regular file. If you need your + ext4 image be named `-`, then just explicitly pass the relative path to it + like `./-`. If `SOURCE_DATE_EPOCH` is set, the filesystem UUID and hash\_seed + will be set to a UUID derived from SOURCE\_DATE\_EPOCH to create reproducible + images. + +- **null** + + A temporary chroot directory will be created in `$TMPDIR` or `/tmp` if + `$TMPDIR` is not set. After the bootstrap is complete, the temporary chroot + will be deleted without being part of the output. This is most useful when the + desired artifact is generated inside the chroot and it is transferred using + special hooks such as **sync-out**. It is also useful in situations where only + the exit code or stdout or stderr of a process run in a hook is of interest. + +# HOOKS + +This section describes properties of the hook options **--setup-hook**, +**--extract-hook**, **--essential-hook** and **--customize-hook** which are common +to all four of them. Any information specific to each hook is documented under +the specific hook options in the section **OPTIONS**. + +The options can be specified multiple times and the commands are executed in +the order in which they are given on the command line. There are four different +types of hook option arguments. If the argument passed to the hook option +starts with `copy-in`, `copy-out`, `tar-in`, `tar-out`, `upload` or +`download` followed by a space, then the hook is interpreted as a special +hook. Otherwise, if _command_ is an existing executable file from `$PATH` or +if _command_ does not contain any shell metacharacters, then _command_ is +directly exec-ed with the path to the chroot directory passed as the first +argument. Otherwise, _command_ is executed under _sh_ and the chroot +directory can be accessed via _$1_. Background (daemon) processes spawned in +a hook are not guaranteed to persist beyond the hook that created them. + +Most environment variables set by **mmdebstrap** (like `DEBIAN_FRONTEND`, +`LC_ALL` and `PATH`) are preserved. Most notably, `APT_CONFIG` is being +unset. If you need the path to `APT_CONFIG` as written by mmdebstrap it can be +found in the `MMDEBSTRAP_APT_CONFIG` environment variable. All environment +variables set by the user are preserved, except for `TMPDIR` which is cleared. +See section **TMPDIR**. Furthermore, `MMDEBSTRAP_MODE` will store the mode set +by **--mode**, `MMDEBSTRAP_FORMAT` stores the format chosen by **--format**, +`MMDEBSTRAP_HOOK` stores which hook is currently run (setup, extract, +essential, customize), `MMDEBSTRAP_ARGV0` stores the name of the binary with +which **mmdebstrap** was executed and `MMDEBSTRAP_VERBOSITY` stores the +numerical verbosity level (0 for no output, 1 for normal, 2 for verbose and 3 +for debug output). The `MMDEBSTRAP_INCLUDE` variable stores the list of +packages, apt patterns or file paths given by the **--include** option, +separated by a comma and with commas and percent signs in the option values +urlencoded. If _SUITE_ name was supplied, it's stored in `MMDEBSTRAP_SUITE`. + +In special hooks, the paths inside the chroot are relative to the root +directory of the chroot. The path on the outside is relative to current +directory of the original **mmdebstrap** invocation. The path inside the chroot +must already exist. Paths outside the chroot are created as necessary. + +In **fakechroot** mode, `tar`, or `sh` and `cat` have to be run inside the +chroot or otherwise, symlinks will be wrongly resolved and/or permissions will +be off. This means that the special hooks might fail in **fakechroot** mode for +the **setup** hook or for the **extract** and **custom** variants if no `tar` or +`sh` and `cat` is available inside the chroot. + +- **copy-out** _pathinside_ \[_pathinside_ ...\] _pathoutside_ + + Recursively copies one or more files and directories recursively from + _pathinside_ inside the chroot to _pathoutside_ outside of the chroot. + +- **copy-in** _pathoutside_ \[_pathoutside_ ...\] _pathinside_ + + Recursively copies one or more files and directories into the chroot into, + placing them into _pathinside_ inside of the chroot. + +- **sync-out** _pathinside_ _pathoutside_ + + Recursively copy everything inside _pathinside_ inside the chroot into + _pathoutside_. In contrast to **copy-out**, this command synchronizes the + content of _pathinside_ with the content of _pathoutside_ without deleting + anything from _pathoutside_ but overwriting content as necessary. Use this + command over **copy-out** if you don't want to create a new directory outside + the chroot but only update the content of an existing directory. + +- **sync-in** _pathoutside_ _pathinside_ + + Recursively copy everything inside _pathoutside_ into _pathinside_ inside the + chroot. In contrast to **copy-in**, this command synchronizes the content of + _pathoutside_ with the content of _pathinside_ without deleting anything from + _pathinside_ but overwriting content as necessary. Use this command over + **copy-in** if you don't want to create a new directory inside the chroot but + only update the content of an existing directory. + +- **tar-in** _outside.tar_ _pathinside_ + + Unpacks a tarball _outside.tar_ from outside the chroot into a certain + location _pathinside_ inside the chroot. In **unshare** mode, device nodes + cannot be created. To ignore device nodes in tarballs, use + **--skip=tar-in/mknod**. + +- **tar-out** _pathinside_ _outside.tar_ + + Packs the path _pathinside_ from inside the chroot into a tarball, placing it + into a certain location _outside.tar_ outside the chroot. To emulate behaviour + of `cp` and to provide control over the path which gets put into the tarball, + a `chdir()` is performed to the `dirname()` of _pathinside_ and then the + `basename()` of _pathinside_ is packaged as a tarball. For example, if + _pathinside_ is `/boot/.` then first a `chdir()` into `/boot` will be + performed before packing up the contents of `.` inside `/boot`. + +- **download** _fileinside_ _fileoutside_ + + Copy the file given by _fileinside_ from inside the chroot to outside the + chroot as _fileoutside_. In contrast to **copy-out**, this command only + handles files and not directories. To copy a directory recursively out of the + chroot, use **copy-out** or **tar-out**. Its advantage is, that by being able to + specify the full path on the outside, including the filename, the file on the + outside can have a different name from the file on the inside. In contrast to + **copy-out** and **tar-out**, this command follows symlinks. + +- **upload** _fileoutside_ _fileinside_ + + Copy the file given by _fileoutside_ from outside the chroot to inside the + chroot as _fileinside_. In contrast to **copy-in**, this command only + handles files and not directories. To copy a directory recursively into the + chroot, use **copy-in** or **tar-in**. Its advantage is, that by being able to + specify the full path on the inside, including the filename, the file on the + inside can have a different name from the file on the outside. In contrast to + **copy-in** and **tar-in**, permission and ownership information will not be + retained. + +# OPERATION + +This section gives an overview of the different steps to create a chroot. At +its core, what **mmdebstrap** does can be put into a 14 line shell script: + + mkdir -p "$2/etc/apt" "$2/var/cache" "$2/var/lib" + cat << END > "$2/apt.conf" + Apt::Architecture "$(dpkg --print-architecture)"; + Apt::Architectures "$(dpkg --print-architecture)"; + Dir "$(cd "$2" && pwd)"; + Dir::Etc::Trusted "$(eval "$(apt-config shell v Dir::Etc::Trusted/f)"; printf "$v")"; + Dir::Etc::TrustedParts "$(eval "$(apt-config shell v Dir::Etc::TrustedParts/d)"; printf "$v")"; + END + echo "deb http://deb.debian.org/debian/ $1 main" > "$2/etc/apt/sources.list" + APT_CONFIG="$2/apt.conf" apt-get update + APT_CONFIG="$2/apt.conf" apt-get --yes --download-only install '?essential' + for f in "$2"/var/cache/apt/archives/*.deb; do dpkg-deb --extract "$f" "$2"; done + chroot "$2" sh -c "dpkg --install --force-depends /var/cache/apt/archives/*.deb" + +The additional complexity of **mmdebstrap** is to support operation without +superuser privileges, bit-by-bit reproducible output, hooks and foreign +architecture support. + +The remainder of this section explains what **mmdebstrap** does step-by-step. + +- **check** + + Upon startup, several checks are carried out, like: + + - whether required utilities (apt, dpkg, tar) are installed + - which mode to use and whether prerequisites are met + - do not allow chrootless mode as root (without fakeroot) unless inside a chroot. This check can be disabled using **--skip=check/chrootless** + - whether the requested architecture can be executed (requires arch-test) using qemu binfmt\_misc support. This requires arch-test and can be disabled using **--skip=check/qemu** + - how the apt sources can be assembled from _SUITE_, _MIRROR_ and **--components** and/or from standard input as deb822 or one-line format and whether the required GPG keys exist. + - which output format to pick depending on the **--format** argument or name of _TARGET_ or its type. + - whether the output directory is empty. This check can be disabled using **--skip=check/empty** + - whether adding a `signed-by` to `apt/sources.list` is necessary. This requires gpg and can be disabled using **--skip=check/signed-by** + +- **setup** + + The following tasks are carried out unless **--skip=setup** is used: + + - create required directories + - write out the temporary apt config file + - populates `/etc/apt/apt.conf.d/99mmdebstrap` and `/etc/dpkg/dpkg.cfg.d/99mmdebstrap` with config options from **--aptopt** and **--dpkgopt**, respectively + - write out `/etc/apt/sources.list` + - copy over `/etc/resolv.conf` and `/etc/hostname` + - populate `/dev` if mknod is possible + +- **setup-hook** + + Run **--setup-hook** options and all `setup*` scripts in **--hook-dir**. + +- **update** + + Runs `apt-get update` using the temporary apt configuration file created in + the **setup** step. This can be disabled using **--skip=update**. + +- **download** + + In the **extract** and **custom** variants, `apt-get install` is used to + download all the packages requested via the **--include** option. In the + remaining variants, apt patterns are used to find the `Essential:yes` packages + from the native architecture. If _SUITE_ is a non-empty string, then only + packages from the archive with suite or codename matching _SUITE_ will be + considered for selection of `Essential:yes` packages. + +- **mount** + + Mount relevant device nodes, `/proc` and `/sys` into the chroot and unmount + them afterwards. This can be disabled using **--skip=chroot/mount** or + specifically by **--skip=chroot/mount/dev**, **--skip=chroot/mount/proc** and + **--skip=chroot/mount/sys**, respectively. **mmdebstrap** will disable running + services by temporarily moving `/usr/sbin/policy-rc.d` and + `/usr/sbin/start-stop-daemon` if they exist. This can be disabled with + **--skip=chroot/policy-rc.d** and **--skip=chroot/start-stop-daemon**, + respectively. + +- **extract** + + Extract the downloaded packages into the rootfs. + +- **prepare** + + In **fakechroot** mode, environment variables `LD_LIBRARY_PATH` will be set up + correctly. For foreign **fakechroot** environments, `LD_LIBRARY_PATH` and + `QEMU_LD_PREFIX` are set up accordingly. This step is not carried out in + **extract** mode and neither for the **chrootless** variant. + +- **extract-hook** + + Run **--extract-hook** options and all `extract*` scripts in **--hook-dir**. + +- **essential** + + Uses `dpkg --install` to properly install all packages that have been + extracted before. Removes all packages downloaded in the **download** step, + except those which were present in `/var/cache/apt/archives/` before (if any). + This can be disabled using **--skip=essential/unlink**. This step is not carried + out in **extract** mode. + +- **essential-hook** + + Run **--essential-hook** options and all `essential*` scripts in **--hook-dir**. + This step is not carried out in **extract** mode. + +- **install** + + Install the apt package into the chroot, if necessary and then run apt from + inside the chroot to install all remaining packages. This step is not carried + out in **extract** mode. + +- **customize-hook** + + Run **--customize-hook** options and all `customize*` scripts in **--hook-dir**. + This step is not carried out in **extract** mode. + +- **unmount** + + Unmount everything that was mounted during the **mount** stage and restores + `/usr/sbin/policy-rc.d` and `/usr/sbin/start-stop-daemon` if necessary. + +- **zombie-reaping** + + Wait for (reap) still running processes (background processes or zombie + processes), unless **--skip=zombie-reaping** is used. + +- **cleanup** + + Performs cleanup tasks, unless **--skip=cleanup** is used: + + - Removes the package lists (unless **--skip=cleanup/apt/lists**) and apt cache (unless **--skip=cleanup/apt/cache**). Both removals can be disabled by using **--skip=cleanup/apt**. + - Remove all files that were put into the chroot for setup purposes, like `/etc/apt/apt.conf.d/00mmdebstrap` and the temporary apt config. This can be disabled using **--skip=cleanup/mmdebstrap**. + - Remove files that make the result unreproducible and write the empty string to /etc/machine-id if it exists. This can be disabled using **--skip=cleanup/reproducible**. Note that this will not remove files that make the result unreproducible on machines with differing `/etc/resolv.conf` or `/etc/hostname`. Use a **--customize-hook** to make those two files reproducible across multiple hosts. See section `SOURCE_DATE_EPOCH` for more information. The following files will be removed: + - `/var/log/dpkg.log` + - `/var/log/apt/history.log` + - `/var/log/apt/term.log` + - `/var/log/alternatives.log` + - `/var/cache/ldconfig/aux-cache` + - `/var/log/apt/eipp.log.xz` + - `/var/lib/dbus/machine-id` + - Remove everything in `/run` inside the chroot. This can be disabled using **--skip=cleanup/run**. + - Remove everything in `/tmp` inside the chroot. This can be disabled using **--skip=cleanup/tmp**. + +- **output** + + For formats other than **directory**, pack up the temporary chroot directory + into a tarball, ext2 image, ext4 image or squashfs image and delete the + temporary chroot directory. + + If **--skip=output/dev** is added, the resulting chroot will not contain the + device nodes, directories and symlinks that **debootstrap** creates but just + an empty /dev as created by **base-files**. + + If **--skip=output/mknod** is added, the resulting chroot will not contain + device nodes (neither block nor character special devices). This is useful + if the chroot tarball is to be exatracted in environments where mknod does + not function like in unshared user namespaces. + +# EXAMPLES + +Use like debootstrap: + + $ sudo mmdebstrap unstable ./unstable-chroot + +Without superuser privileges: + + $ mmdebstrap unstable unstable-chroot.tar + +With no command line arguments at all. The chroot content is entirely defined +by a sources.list file on standard input. + + $ mmdebstrap < /etc/apt/sources.list > unstable-chroot.tar + +Since the tarball is output on stdout, members of it can be excluded using tar +on-the-fly. For example the /dev directory can be removed from the final +tarbal in cases where it is to be extracted by a non-root user who cannot +create device nodes: + + $ mmdebstrap unstable | tar --delete ./dev > unstable-chroot.tar + +Create a tarball for use with `sbuild --chroot-mode=unshare`: + + $ mmdebstrap --variant=buildd unstable ~/.cache/sbuild/unstable-amd64.tar + +Create a buildd tarball for EOL distro: + + $ mmdebstrap --include=apt --variant=buildd --aptopt \ + 'Apt::Key::gpgvcommand "/usr/libexec/mmdebstrap/gpgvnoexpkeysig"' \ + --aptopt='Acquire::Check-Valid-Until "false"' \ + --keyring=/usr/share/keyrings/debian-archive-removed-keys.gpg \ + --hook-dir=/usr/share/mmdebstrap/hooks/maybe-jessie-or-older \ + jessie ~/.cache/sbuild/jessie-arm64.tar \ + http://archive.debian.org/debian + +Instead of a tarball, a squashfs image can be created: + + $ mmdebstrap unstable unstable-chroot.squashfs + +By default, **mmdebstrap** runs **tar2sqfs** with `--no-skip --exportable +\--compressor xz --block-size 1048576`. To choose a different set of options, +and to filter out all extended attributes not supported by **tar2sqfs**, pipe +the output of **mmdebstrap** into **tar2sqfs** manually like so: + + $ mmdebstrap unstable \ + | mmtarfilter --pax-exclude='*' \ + --pax-include='SCHILY.xattr.user.*' \ + --pax-include='SCHILY.xattr.trusted.*' \ + --pax-include='SCHILY.xattr.security.*' \ + | tar2sqfs --quiet --no-skip --force --exportable --compressor xz \ + --block-size 1048576 unstable-chroot.squashfs + +By default, debootstrapping a stable distribution will add mirrors for security +and updates to the sources.list. + + $ mmdebstrap stable stable-chroot.tar + +If you don't want this behaviour, you can override it by manually specifying a +mirror in various different ways: + + $ mmdebstrap stable stable-chroot.tar http://deb.debian.org/debian + $ mmdebstrap stable stable-chroot.tar "deb http://deb.debian.org/debian stable main" + $ mmdebstrap stable stable-chroot.tar /path/to/sources.list + $ mmdebstrap stable stable-chroot.tar - < /path/to/sources.list + +Drop locales (but not the symlink to the locale name alias database), +translated manual packages (but not the untranslated ones), and documentation +(but not copyright and Debian changelog). + + $ mmdebstrap --variant=essential \ + --dpkgopt='path-exclude=/usr/share/man/*' \ + --dpkgopt='path-include=/usr/share/man/man[1-9]/*' \ + --dpkgopt='path-exclude=/usr/share/locale/*' \ + --dpkgopt='path-include=/usr/share/locale/locale.alias' \ + --dpkgopt='path-exclude=/usr/share/doc/*' \ + --dpkgopt='path-include=/usr/share/doc/*/copyright' \ + --dpkgopt='path-include=/usr/share/doc/*/changelog.Debian.*' \ + unstable debian-unstable.tar + +Create a bootable USB Stick that boots into a full Debian desktop: + + $ mmdebstrap --aptopt='Apt::Install-Recommends "true"' --customize-hook \ + 'chroot "$1" adduser --comment user --disabled-password user' \ + --customize-hook='echo 'user:live' | chroot "$1" chpasswd' \ + --customize-hook='echo host > "$1/etc/hostname"' \ + --customize-hook='echo "127.0.0.1 localhost host" > "$1/etc/hosts"' \ + --include=linux-image-amd64,task-desktop unstable debian-unstable.tar + $ cat << END > extlinux.conf + > default linux + > timeout 0 + > + > label linux + > kernel /vmlinuz + > append initrd=/initrd.img root=LABEL=rootfs + END + # You can use $(sudo blockdev --getsize64 /dev/sdXXX) to get the right + # image size for the target medium in bytes + $ guestfish -N debian-unstable.img=disk:8G -- \ + part-disk /dev/sda mbr : \ + part-set-bootable /dev/sda 1 true : \ + mkfs ext4 /dev/sda1 : \ + set-label /dev/sda1 rootfs : \ + mount /dev/sda1 / : \ + tar-in debian-unstable.tar / xattrs:true : \ + upload /usr/lib/EXTLINUX/mbr.bin /boot/mbr.bin : \ + copy-file-to-device /boot/mbr.bin /dev/sda size:440 : \ + extlinux / : copy-in extlinux.conf / : sync : umount / : shutdown + $ qemu-system-x86_64 -m 1G -enable-kvm debian-unstable.img + $ sudo dd if=debian-unstable.img of=/dev/sdXXX status=progress + +On architectures without extlinux you can also boot using grub2: + + $ mmdebstrap --include=linux-image-amd64,grub2,systemd-sysv unstable fs.tar + $ guestfish -N debian-unstable.img=disk:2G -- \ + part-disk /dev/sda mbr : \ + part-set-bootable /dev/sda 1 true : \ + mkfs ext4 /dev/sda1 : \ + set-label /dev/sda1 rootfs : \ + mount /dev/sda1 / : \ + tar-in fs.tar / xattrs:true : \ + command "grub-install /dev/sda" : \ + command update-grub : \ + sync : umount / : shutdown + $ qemu-system-x86_64 -m 1G -enable-kvm debian-unstable.img + +Build libdvdcss2.deb without installing installing anything or changing apt +sources on the current system: + + $ mmdebstrap --variant=apt --components=main,contrib --include=libdvd-pkg \ + --customize-hook='chroot $1 /usr/lib/libdvd-pkg/b-i_libdvdcss.sh' \ + | tar --extract --verbose --strip-components=4 \ + --wildcards './usr/src/libdvd-pkg/libdvdcss2_*_*.deb' + $ ls libdvdcss2_*_*.deb + +Use as replacement for autopkgtest-build-qemu and vmdb2 for all architectures +supporting EFI booting (amd64, arm64, armhf, i386, riscv64), use a convenience +wrapper around **mmdebstrap**: + + $ mmdebstrap-autopkgtest-build-qemu unstable ./autopkgtest.img + +Use as replacement for autopkgtest-build-qemu and vmdb2 on architectures +supporting extlinux (amd64 and i386): + + $ mmdebstrap --variant=important --include=linux-image-amd64 \ + --customize-hook='chroot "$1" passwd --delete root' \ + --customize-hook='chroot "$1" useradd --home-dir /home/user --create-home user' \ + --customize-hook='chroot "$1" passwd --delete user' \ + --customize-hook='echo host > "$1/etc/hostname"' \ + --customize-hook='echo "127.0.0.1 localhost host" > "$1/etc/hosts"' \ + --customize-hook=/usr/share/autopkgtest/setup-commands/setup-testbed \ + unstable debian-unstable.tar + $ cat << END > extlinux.conf + > default linux + > timeout 0 + > + > label linux + > kernel /vmlinuz + > append initrd=/initrd.img root=/dev/vda1 rw console=ttyS0 + END + $ guestfish -N debian-unstable.img=disk:8G -- \ + part-disk /dev/sda mbr : \ + part-set-bootable /dev/sda 1 true : \ + mkfs ext4 /dev/sda1 : mount /dev/sda1 / : \ + tar-in debian-unstable.tar / xattrs:true : \ + upload /usr/lib/EXTLINUX/mbr.bin /boot/mbr.bin : \ + copy-file-to-device /boot/mbr.bin /dev/sda size:440 : \ + extlinux / : copy-in extlinux.conf / : sync : umount / : shutdown + $ qemu-img convert -O qcow2 debian-unstable.img debian-unstable.qcow2 + +As a debootstrap wrapper to run it without superuser privileges but using Linux +user namespaces instead. This fixes Debian bug #829134. + + $ mmdebstrap --variant=custom --mode=unshare \ + --setup-hook='debootstrap unstable "$1"' \ + - debian-debootstrap.tar + +Build a non-Debian chroot like Ubuntu bionic: + + $ mmdebstrap --aptopt='Dir::Etc::Trusted + "/usr/share/keyrings/ubuntu-keyring-2012-archive.gpg"' bionic bionic.tar + +If, for some reason, you cannot use a caching proxy like apt-cacher or +apt-cacher-ng, you can use the **sync-in** and **sync-out** special hooks to +synchronize a directory outside the chroot with `/var/cache/apt/archives` +inside the chroot. + + $ mmdebstrap --variant=apt --skip=essential/unlink \ + --setup-hook='mkdir -p ./cache "$1"/var/cache/apt/archives/' \ + --setup-hook='sync-in ./cache /var/cache/apt/archives/' \ + --customize-hook='sync-out /var/cache/apt/archives ./cache' \ + unstable /dev/null + +Instead of copying potentially large amounts of data with **sync-in** you can +also use a bind-mount in combination with a `file://` mirror to make packages +from the outside available inside the chroot: + + $ mmdebstrap --variant=apt --skip=essential/unlink \ + --setup-hook='mkdir "$1/tmp/mirror"' \ + --setup-hook='mount -o ro,bind /tmp/mirror "$1/tmp/mirror"' \ + --customize-hook='sync-out /var/cache/apt/archives ./cache' \ + --customize-hook='umount "$1/tmp/mirror"; rmdir "$1/tmp/mirror";' \ + unstable /dev/null file:///tmp/mirror http://deb.debian.org/debian + +To automatically mount all directories referenced by `file://` mirrors +into the chroot you can use a hook: + + $ mmdebstrap --variant=apt \ + --hook-dir=/usr/share/mmdebstrap/hooks/file-mirror-automount \ + unstable /dev/null file:///tmp/mirror1 file:///tmp/mirror2 + +Create a system that can be used with docker: + + $ mmdebstrap unstable | sudo docker import - debian + [...] + $ sudo docker run -it --rm debian whoami + root + $ sudo docker rmi debian + +Create and boot a qemu virtual machine for an arbitrary architecture using +the **debvm-create** wrapper script around **mmdebstrap**: + + $ debvm-create -r stable -- --architecture=riscv64 + $ debvm-run + +Create a system that can be used with podman: + + $ mmdebstrap unstable | podman import - debian + [...] + $ podman run --network=none -it --rm debian whoami + root + $ podman rmi debian + +As a docker/podman replacement: + + $ mmdebstrap unstable chroot.tar + [...] + $ mmdebstrap --variant=custom --skip=update,tar-in/mknod \ + --setup-hook='tar-in chroot.tar /' \ + --customize-hook='chroot "$1" whoami' unstable /dev/null + [...] + root + $ rm chroot.tar + +You can re-use a chroot tarball created with mmdebstrap for further refinement. +Say you want to create a minimal chroot and a chroot with more packages +installed, then instead of downloading and installing the essential packages +twice you can instead build on top of the already present minimal chroot: + + $ mmdebstrap --variant=apt unstable chroot.tar + $ mmdebstrap --variant=custom --skip=update,setup,cleanup,tar-in/mknod \ + --setup-hook='tar-in chroot.tar /' \ + --customize-hook='chroot "$1" apt-get install --yes pkg1 pkg2' \ + '' chroot-full.tar + +# ENVIRONMENT VARIABLES + +- `SOURCE_DATE_EPOCH` + + By setting `SOURCE_DATE_EPOCH` the result will be reproducible across multiple + runs with the same options and mirror content. Note that for debootstrap + compatibility, **mmdebstrap** will copy the host's `/etc/resolv.conf` and + `/etc/hostname` into the chroot. This means that the **mmdebstrap** output will + differ if it is run on machines with differing `/etc/resolv.conf` and + `/etc/hostname` contents. To make the result reproducible across different + hosts, you need to manually either delete both files from the output: + + $ mmdebstrap --customize-hook='rm "$1"/etc/resolv.conf' \ + --customize-hook='rm "$1"/etc/hostname' ... + + or fill them with reproducible content: + + $ mmdebstrap --customize-hook='echo nameserver X > "$1"/etc/resolv.conf' \ + --customize-hook='echo host > "$1"/etc/hostname' ... + +- `TMPDIR` + + When creating a tarball, a temporary directory is populated with the rootfs + before the tarball is packed. The location of that temporary directory will be + in `/tmp` or the location pointed to by `TMPDIR` if that environment variable + is set. Setting `TMPDIR` to a different directory than `/tmp` is useful if + you have `/tmp` on a tmpfs that is too small for your rootfs. + + If you set `TMPDIR` in **unshare** mode, then the unshared user must be able to + access the directory. This means that the directory itself must be + world-writable and all its ancestors must be at least world-executable. + + Since `TMPDIR` is only valid outside the chroot, the variable is being unset + when running hook scripts. If you need a valid temporary directory in a hook, + consider using `/tmp` inside your target directory. + +# DEBOOTSTRAP + +This section lists some differences to debootstrap. + +- More than one mirror possible +- Default mirrors for stable releases include updates and security mirror +- Multiple ways to operate as non-root: fakechroot and unshare +- twice as fast +- Can create a chroot with only `Essential:yes` packages and their deps +- Reproducible output by default if $SOURCE\_DATE\_EPOCH is set +- Can create output on filesystems with nodev set +- apt cache and lists are cleaned at the end +- foreign architecture chroots using qemu-user + +Limitations in comparison to debootstrap: + +- Only runs on systems with apt installed (Debian and derivatives) +- No _SCRIPT_ argument (use hooks instead) +- Some debootstrap options don't exist, namely: + + _--second-stage_, _--exclude_, _--resolve-deps_, _--force-check-gpg_, + _--merged-usr_, _--no-merged-usr_ and _--cache-dir_. + +# MERGED-/USR + +**mmdebstrap** will create a merged-/usr chroot or not depending on whether +packages setting up merged-/usr (i.e. the **usrmerge** package) are installed or +not. In Debian, the essential package **init-system-helpers** depends on the +**usrmerge** package, starting with Debian 12 (Bookworm). + +Before Debian 12 (Bookworm), to force **mmdebstrap** to create a chroot with +merged-/usr using symlinks, either explicitly install the **usrmerge** package: + + --include=usrmerge + +or setup merged-/usr using the debootstrap-method which takes care of the +architecture specific symlinks and installs the **usr-is-merged** package. + + --hook-dir=/usr/share/mmdebstrap/hooks/merged-usr + +To force **mmdebstrap** to create a chroot without merged-/usr even after the +Debian 12 (Bookworm) release, you can use the following hook: + + --hook-dir=/usr/share/mmdebstrap/hooks/no-merged-usr + +This will write "this system will not be supported in the future" into +`/etc/unsupported-skip-usrmerge-conversion` inside the chroot and install the +**usr-is-merged** package to avoid the installation of the **usrmerge** package +and its dependencies. + +If you are using **mmdebstrap** in a setup where you do not know upfront whether +the chroot you are creating should be merged-/usr or not and you want to avoid +installation of the **usrmerge** package and it's dependencies, you can use: + + --hook-dir=/usr/share/mmdebstrap/hooks/maybe-merged-usr + +That hook will use the availability of the **usr-is-merged** package to decide +whether to call the **merged-usr** hook or not. + +# COMPRESSION + +**mmdebstrap** will choose a suitable compressor for the output tarball +depending on the filename extension. The following mapping from filename +extension to compressor applies: + + extension compressor + -------------------- + .tar none + .gz gzip + .tgz gzip + .taz gzip + .Z compress + .taZ compress + .bz2 bzip2 + .tbz bzip2 + .tbz2 bzip2 + .tz2 bzip2 + .lz lzip + .lzma lzma + .tlz lzma + .lzo lzop + .lz4 lz4 + .xz xz + .txz xz + .zst zstd + +To change compression specific options, either use the respecitve environment +variables like **XZ\_OPT** or send **mmdebstrap** output to your compressor of +choice with a pipe. + +# WRAPPERS + +## debvm + +**debvm** helps create and run virtual machines for various Debian releases and +architectures. The tool **debvm-create** can be used to create a virtual +machine image and the tool **debvm-run** can be used to run such a machine +image. Their purpose primarily is testing software using qemu as a containment +technology. These are relatively thin wrappers around **mmdebstrap** and +**qemu**. + +## bdebstrap + +**bdebstrap** is a YAML config based multi-mirror Debian chroot creation tool. +**bdebstrap** is an alternative to **debootstrap** and a wrapper around +**mmdebstrap** to support YAML based configuration files. It inherits all +benefits from **mmdebstrap**. The support for configuration allows storing all +customization in a YAML file instead of having to use a very long one-liner +call to **mmdebstrap**. It also layering multiple customizations on top of each +other, e.g. to support flavors of an image. + +# BUGS + +https://gitlab.mister-muffin.de/josch/mmdebstrap/issues + +https://bugs.debian.org/src:mmdebstrap + +As of version 1.20.9, dpkg does not provide facilities preventing it from +reading the dpkg configuration of the machine running **mmdebstrap**. +Therefore, until this dpkg limitation is fixed, a default dpkg configuration is +recommended on machines running **mmdebstrap**. If you are using **mmdebstrap** +as the non-root user, then as a workaround you could run `chmod 600 +/etc/dpkg/dpkg.cfg.d/*` so that the config files are only accessible by the +root user. See Debian bug #808203. + +With apt versions before 2.1.16, setting `[trusted=yes]` or +`Acquire::AllowInsecureRepositories "1"` to allow signed archives without a +known public key or unsigned archives will fail because of a gpg warning in the +apt output. Since apt does not communicate its status via any other means than +human readable strings, and because **mmdebstrap** wants to treat transient +network errors as errors, **mmdebstrap** treats any warning from "apt-get +update" as an error. + +# SEE ALSO + +[debootstrap(8)](http://man.he.net/man8/debootstrap), [debvm(1)](http://man.he.net/man1/debvm), [bdebstrap(1)](http://man.he.net/man1/bdebstrap) \ No newline at end of file