This should work on modern linux distributions even when running in WSL.
systemd-container)debootstrap must be installed. (usually you just need to install the package debootstrap) /var/lib/machines present (should be there after systemd-container install)debootstrap --arch=amd64 --include=dbus,libpam-systemd,locales stable /var/lib/machines/openwrt-builder http://deb.debian.org/debian
echo 'openwrt-builder' > /var/lib/machines/openwrt-builder/etc/hostname
systemd-nspawn --settings=false -D "/var/lib/machines/openwrt-builder" /bin/bash -c 'sed -i "s@^# \(en_US*\)@\1@g" /etc/locale.gen' systemd-nspawn --settings=false -D "/var/lib/machines/openwrt-builder" /bin/bash -c 'locale-gen' echo -e 'LANG=en_US.UTF-8\nLC_ALL=en_US.UTF-8' > /var/lib/machines/openwrt-builder/etc/default/locale.conf
# systemd-nspawn --settings=false -D "/var/lib/machines/openwrt-builder" /bin/bash -c 'apt -y install build-essential clang flex bison g++ gawk gpg gcc-multilib g++-multilib gettext git libncurses5-dev libssl-dev python3-setuptools rsync swig unzip zlib1g-dev file wget vim' systemd-nspawn --settings=false -D "/var/lib/machines/openwrt-builder" /bin/bash -c 'apt -y install build-essential clang flex bison g++ gpg gcc-multilib g++-multilib python3-setuptools swig libncurses-dev zlib1g-dev gawk git gettext libssl-dev xsltproc rsync file wget vim unzip python3'
systemd-nspawn --settings=false -D "/var/lib/machines/openwrt-builder" useradd -m -s /bin/bash builder > /dev/null 2>&1 || true
NSPAWN_FILE="/etc/systemd/nspawn/openwrt-builder.nspawn" cat > "$NSPAWN_FILE" <<EOF [Exec] Boot=true PrivateUsers=no Hostname=openwrt-builder [Network] Private=no VirtualEthernet=no [Files] EOF
You should now be able to start (essentially it is nearly a boot) the machine with the following command:
machinectl start openwrt-builder
machinectl shell builder@openwrt-builder
You should now see the following output (being inside the machine):
builder@openwrt-builder:~$
You can now start building OpenWRT.
Type exit to exit/logout from the machine.
builder@openwrt-builder:~$ exit logout Connection to machine openwrt-builder terminated.
Yes, the machine/container did a real boot so we have to shut it down:
To see that it is running type machinectl list:
MACHINE CLASS SERVICE OS VERSION ADDRESSES openwrt-builder container systemd-nspawn debian 13- 1 machines listed.
To shut it down type:
machinectl stop openwrt-builder
You can save your work by creating a full, re-importable export of your machine and a copy of the file /etc/systemd/nspawn/openwrt-builder.nspawn.
In the following example we will export the machine to the file /root/openwrt-builder.tar.gz
# machinectl export-tar openwrt-builder /root/openwrt-builder.tar.xz # smaller file size takes longer machinectl export-tar openwrt-builder /root/openwrt-builder.tar.gz
Output:
Enqueued transfer job 1. Press C-c to continue download in background. Exporting '/var/lib/machines/openwrt-builder', saving to '/root/openwrt-builder.tar.gz' with compression 'gzip'. Operation completed successfully. Exiting.
Make sure, to backup a copy of /etc/systemd/nspawn/openwrt-builder.nspawn
cp /etc/systemd/nspawn/openwrt-builder.nspawn /root/openwrt-builder.nspawn
This command will destroy the machine!! The folder /var/lib/machines/openwrt-builder and the file /etc/systemd/nspawn/openwrt-builder.nspawn will be deleted!
machinectl remove openwrt-builder
Use the archive from a previous export, e.g. openwrt-builder.tar.gz and import it.
Make sure, that machine openwrt-builder2 does not exist or use a different name
machinectl import-tar /root/openwrt-builder.tar.gz openwrt-builder2
Output:
Enqueued transfer job 1. Press C-c to continue download in background. Importing '/var/lib/machines/openwrt-builder.tar.gz', saving as 'openwrt-builder2'. Imported 0%. Imported 1%. Imported 2%. Imported 5%. Imported 10% ... ... Imported 99%. Operation completed successfully. Exiting.
Copy your backup of <machine-name>.nspawn file to /etc/systemd/nspawn/<machine-name>.nspawn.
cp /root/openwrt-builder.nspawn /etc/systemd/nspawn/openwrt-builder2.nspawn
Everything should be restored!
You can now again startup the container/machine using machinectl start openwrt-builder2.
The following script (to be executed as root or with sudo) should do all of the above automatically and should work on Debian, Ubuntu, archlinux, Manjaro, RHEL, CentOS, Fedora and almalinux.
#!/usr/bin/env bash # Desired hostname for the container HOSTNAME_VAR="container" BASEDIR="/var/lib/machines" CONTAINERNAME="openwrt-builder" TEMPDIR="${BASEDIR}/${CONTAINERNAME}" check_systemd_container() { if command -v systemd-nspawn &> /dev/null; then echo "systemd-container capability (systemd-nspawn) is already available." return 0 else echo "systemd-container capability (systemd-nspawn) is NOT available." return 1 fi } install_systemd_container_debian() { apt-get -y update apt-get install -y systemd-container } install_systemd_container_arch() { pacman -Sy --noconfirm systemd } install_systemd_container_rhel() { if command -v dnf &> /dev/null; then dnf install -y systemd-container || echo "systemd-container package not available in dnf repositories" elif command -v yum &> /dev/null; then yum install -y systemd-container || echo "systemd-container package not available in yum repositories" else echo "dnf or yum package manager not found" fi } install_debian() { apt-get install -y debootstrap } install_arch() { pacman -Sy --noconfirm debootstrap } install_rhel() { if command -v dnf &> /dev/null; then dnf install -y debootstrap || echo "debootstrap not available in dnf repositories" elif command -v yum &> /dev/null; then yum install -y debootstrap || echo "debootstrap not available in yum repositories" else echo "dnf or yum package manager not found" fi } if [ -f /etc/os-release ]; then . /etc/os-release else echo "/etc/os-release not found. Cannot detect OS." exit 1 fi echo "Detected distribution: $ID" if ! check_systemd_container; then echo "Attempting to install systemd-container capability..." case "$ID" in debian|ubuntu) install_systemd_container_debian ;; arch|manjaro) install_systemd_container_arch ;; rhel|centos|fedora|almalinux) install_systemd_container_rhel ;; *) echo "Unsupported distribution: $ID" exit 1 ;; esac fi if command -v debootstrap &> /dev/null; then echo "debootstrap is already installed." else echo "Installing debootstrap..." case "$ID" in debian|ubuntu) install_debian ;; arch|manjaro) install_arch ;; rhel|centos|fedora|almalinux) install_rhel ;; *) echo "Unsupported distribution: $ID" exit 1 ;; esac fi if [ ! -d /etc/systemd/nspawn ]; then echo -n "Creating directory /etc/systemd/nspawn..." mkdir -p /etc/systemd/nspawn echo "done" else echo "Directory /etc/systemd/nspawn already exists" fi if [ -d "$TEMPDIR" ]; then echo "Container directory $TEMPDIR already exists. Please remove it or choose a different name." exit 1 else echo -n "Creating container directory ${TEMPDIR}..." mkdir -p "$TEMPDIR" echo "done" fi # Set ownership and permissions chown -R root:root "$TEMPDIR" chmod 755 "$TEMPDIR" echo -n "Bootstrapping minimal Debian filesystem in $TEMPDIR with essential packages..." debootstrap --arch=amd64 --include=dbus,libpam-systemd,locales stable "$TEMPDIR" http://deb.debian.org/debian >/dev/null echo "done" echo -n "Setting hostname inside the container filesystem to '${HOSTNAME_VAR}'..." echo "$HOSTNAME_VAR" > "$TEMPDIR/etc/hostname" echo "done" echo -n "Prepare locale.gen, generating locales and create locales.conf ..." systemd-nspawn --settings=false -D "$TEMPDIR" /bin/bash -c 'sed -i "s@^# \(en_US*\)@\1@g" /etc/locale.gen' >/dev/null 2>&1 systemd-nspawn --settings=false -D "$TEMPDIR" /bin/bash -c 'locale-gen' > /dev/null 2>&1 systemd-nspawn --settings=false -D "$TEMPDIR" /bin/bash -c 'echo -e "LANG=en_US.UTF-8\nLC_ALL=en_US.UTF-8" > "/etc/default/locale.conf"' >/dev/null 2>&1 echo "done" echo -n "Installing packages inside container..." systemd-nspawn -D "$TEMPDIR" /bin/bash -c 'apt -y install build-essential clang flex bison g++ gpg gcc-multilib g++-multilib python3-setuptools swig libncurses-dev zlib1g-dev gawk git gettext libssl-dev xsltproc rsync file wget vim unzip python3' > /dev/null 2>&1 echo "done" echo -n "Creating non-privileged user 'builder' with home directory and default shell inside container..." systemd-nspawn -D "$TEMPDIR" useradd -m -s /bin/bash builder > /dev/null 2>&1 echo "done" echo -n "Adding banner message to /etc/profile to display on login shells for id 0 (root)..." cat >> "$TEMPDIR/etc/profile" <<'EOF' if [ "$(id -u)" -eq 0 ]; then echo '##########################################################' echo '# You are now inside the container #' echo '##########################################################' echo 'use the command' echo '' echo ' su - builder' echo '' echo 'to become the non-privileged user "builder".' echo '##########################################################' fi EOF echo "done" NSPAWN_FILE="/etc/systemd/nspawn/${CONTAINERNAME}.nspawn" echo -n "Creating systemd-nspawn config file at $NSPAWN_FILE with hostname ${HOSTNAME_VAR}..." cat > "$NSPAWN_FILE" <<EOF [Exec] Boot=true PrivateUsers=no Hostname=$HOSTNAME_VAR [Network] Private=no VirtualEthernet=no [Files] EOF echo "done" echo "Container directory '$TEMPDIR' set up and ready." echo -e "\n\n##############################################################################\n" echo "You can start the container machine with: machinectl start $CONTAINERNAME" echo "To interact with it, use : machinectl shell $CONTAINERNAME" echo " or" echo " machinectl shell builder@$CONTAINERNAME" echo -e "\n##############################################################################\n"