Home     Blog     Rss     Contact     Donate

Use mmdebstrap to install Debian (and more)


This is a follow up to my Debian Deboostrap Install guide posted a few years back. I will not go through the whole install process again here, you can simply follow the guide as written and replace the debootstrap command with mmdebstrap when you've reached that step.

mmdebstrap - multi-mirror Debian chroot creation


The debootstrap utility is a fantastic tool that allows you to create custom Debian installs as big or as small as you want. It's a bit limited, though. For exmaple, it can only use a single mirror to fetch packages when creating the system (which can be a security risk, among other things).

This is where mmdebstrap comes in. It fixes the security concern through the use of multiple mirrors (including Debian's security mirror) and offers much more customizing options, especially thanks to 'hooks'.

Make no mistake, debootstrap can be as powerful through a good dose of scripting, but mmdebstrap's hooks, and other built in options, offer a much more straightforward way of doing it.

The bread and butter


First, install mmdebstrap:


# apt-get install mmdebstrap -y

If all you want is to create a minimal Debian chroot as in the debootstrap guide linked above, substituting the debootstrap command with mmdebstrap, then the following will suffice:


# /usr/bin/mmdebstrap --variant=minbase \
    --components="main non-free-firmware" \
    --include="usrmerge vim" \
    --skip=check/empty \
    $FLAVOUR $TARGET

Replace $FLAVOUR with the right Debian version (currently, bookworm) and $TARGET with the target directory, usually /mnt when performing a chroot-style install.

--include="usrmerge vim": Debian now has merged usr, and this package will be fetched regardless, but I have encountered situations where not including it caused some issues. Vim is there because having an editor inside the chroot is convenient, but if you prefer something else, go for it. You can add any other packages to --include, but most packages (especially things like the kernel and init system) should be installed either in the chroot or when booted into a new install.

--skip=check/empty: normally, mmdebstrap will fail if the target dir is not empty. This is a problem when doing a chroot install since you will most likely have /home and probably /boot already created and mounted. This flag skips the check, so the command will succeed. You may remove this flag if you are creating a chroot, rather than performing an install.

Hooks


These are commands that get executed by mmdebstrap at various stages of the chroot creation. They are two things, really:

  1. Individual commands passed as options to mmdebstrap, or
  2. Scripts containing multiple commands.

Types of hook:

Hooks as individual commands


Passing a single command as a hook to mmdebstrap is rather simple:


# /usr/bin/mmdebstrap --variant=minbase \
    --components="main non-free-firmware" \
    --include="usrmerge vim" \
    --setup-hook='cp mydir "$1"/etc/'
    --essential-hook='echo tzdata tzdata/Areas select America | chroot "$1" debconf-set-selections'
    --essential-hook='echo tzdata tzdata/Zones/America select Toronto | chroot "$1" debconf-set-selections'
    --customize-hook='echo myhostname > "$1"/etc/hosts' \
    --skip=check/empty \
    $FLAVOUR $TARGET

The --setup-hook would copy a directory (mydir) to the chroot, for example if it contained anything needed during setup.

The two --essential-hook would set the timezone to America/Toronto.

The --customize-hook would set the chroot's hostname.

Note that "$1" represents the $TARGET, always use "$1" whether you're performing a command in the chroot (eg, chroot "$1" somecommand) or copying something to the chroot (eg cp somefile "$1"/etc/skel/). This ensures that the commands will work no matter what $TARGET is.

Hook scripts


Passing commands to mmdebstrap in the above manner suffices when there aren't many, but for extensive customization, using hook scripts become a lot more useful.

First, create a hook directory:


$ mkdir hooks

Next, simply create your scripts, and name them after the specific stage they should be performed in, eg customize01.sh. You can have multiple scripts for each stages, and they would be executed alphabetically, hence the 01. A second script would be named customize02.sh, and so on.

Hook scripts can be in any scripting language as long as its dependencies are available in the chroot, at the stage they are to be executed.

Example customize01.sh script in POSIX sh:


#!/bin/sh

set -e

# Set the default debconf frontend to Readline
echo 'debconf debconf/frontend select Readline' | chroot "$1" debconf-set-selections

# Enable the wheel group.
sed -i '15 s/^# //' "$1"/etc/pam.d/su
chroot "$1" addgroup --system wheel

# Set the system's hostname.
echo "myhostname" > "$1"/etc/hostname

# Set the timezone
echo "tzdata tzdata/Areas select America" \
    | chroot "$1" debconf-set-selections
echo "tzdata tzdata/Zones/America select New_York" \
    | chroot "$1" debconf-set-selections
echo 'tzdata tzdata/Zones/Etc select UTC' \
       | chroot "$1" debconf-set-selections
# This has to be done or else dpkg-reconfigure insists on using Etc
# as the default timezone for whatever stupid reason.
echo "America/New_York" > "$1"/etc/timezone
chroot "$1" ln -sf /usr/share/zoneinfo/America/New_York" /etc/localtime
chroot "$1" dpkg-reconfigure -f noninteractive tzdata

# Set locale
echo "locales locales/default_environment_locale select en_US.UTF-8" \
    | chroot "$1" debconf-set-selections
echo "locales locales/locales_to_be_generated multiselect en_US.UTF-8 UTF-8" \
    | chroot "$1" debconf-set-selections
chroot "$1" apt-get install locales -y

Then chmod +x customize01.sh to make it executable.

Using the script with --hook-directory:


# /usr/bin/mmdebstrap --variant=minbase \
    --components="main non-free-firmware" \
    --include="usrmerge vim" \
    --hook-directory=hooks \
    --skip=check/empty \
    $FLAVOUR $TARGET

There you go. Hooks can be used to setup the whole system the same way you would when performing a chroot-style install.

Creating tarballs


Another useful built-in fuction of mmdebstrap is its ability to create an archive of the chroot automatically. This is especially useful if you want custom chroots that can be used anywhere, or a custom Debian system that can be installed simply by extracting the tarball to /mnt (for example).

To create an archive, simply change the $TARGET from a directory to a tarball, eg:


# /usr/bin/mmdebstrap --variant=minbase \
    --components="main non-free-firmware" \
    --include="usrmerge vim" \
    --hook-directory=hooks \
    $FLAVOUR custom-chroot.tgz

When extracting the chroot, you will need to use the following command in order to make sure the proper permissions are retained:


# tar xzpvf custom-chroot.tgz --xattrs --xattrs-include='*' --numeric-owner -C $TARGET

$TARGET can either be any directory, or /mnt in the case of an install.

Bonus cleanup script


Whether you intend to share your chroot tarball, host it online or simply use it everywhere, it is good practice to clean it up a little.

customize02.sh


#!/bin/sh

set -e

chroot "$1" apt clean
rm -rf "$1"/var/lib/apt/lists/*
rm "$1"/var/log/apt/eipp.log.xz
rm "$1"/var/log/apt/history.log
rm "$1"/var/log/apt/term.log
rm "$1"/var/log/alternatives.log
rm "$1"/var/log/dpkg.log
rm "$1"/etc/resolv.conf
rm "$1"/tmp/*
for _file in /etc/machine-id /var/lib/dbus/machine-id; do
    if [ -f "${1}/${_file}" ]; then
        rm "${1}/${_file}"
    fi
done

Links


[1] mmdebstrap manual page