Last active
October 28, 2024 14:14
-
-
Save sfan5/52aa53f5dca06ac3af30455b203d3404 to your computer and use it in GitHub Desktop.
Create bootable systemd-nspawn containers with Alpine, Arch Linux or Ubuntu
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash -e | |
# Creates a systemd-nspawn container with Alpine | |
MIRROR=http://dl-cdn.alpinelinux.org/alpine | |
VERSION=${VERSION:-v3.20} | |
APKTOOLS_VERSION=2.14.4-r1 | |
wget_or_curl () { | |
if command -v wget >/dev/null; then | |
wget -qO- "$1" | |
elif command -v curl >/dev/null; then | |
curl -Ls "$1" | |
else | |
echo "missing either curl or wget" >&2 | |
return 1 | |
fi | |
} | |
if [ $UID -ne 0 ]; then | |
echo "run this script as root" >&2 | |
exit 1 | |
fi | |
dest="$1" | |
if [ -z "$dest" ]; then | |
echo "Usage: $0 <destination>" >&2 | |
exit 0 | |
fi | |
if [ -e "$dest/usr/bin" ]; then | |
echo "destination already seems to contain a root file system" >&2 | |
exit 1 | |
fi | |
if [[ "$(uname -m)" =~ ^i[3456]86|x86 ]]; then | |
toolarch=x86 | |
guestarch=$toolarch | |
[ "$(uname -m)" = x86_64 ] && guestarch=x86_64 | |
elif [[ "$(uname -m)" =~ ^arm|aarch64 ]]; then | |
toolarch=armv7 | |
guestarch=$toolarch | |
[ "$(uname -m)" = aarch64 ] && guestarch=aarch64 | |
else | |
echo "unsupported architecture" >&2 | |
exit 1 | |
fi | |
apkdir=$(mktemp -d) | |
trap 'rm -rf $apkdir' EXIT | |
wget_or_curl "$MIRROR/latest-stable/main/$toolarch/apk-tools-static-$APKTOOLS_VERSION.apk" \ | |
| tar -xz -C $apkdir || \ | |
{ echo "couldn't download apk-tools, the version might have changed..." >&2; exit 1; } | |
$apkdir/sbin/apk.static \ | |
-X $MIRROR/$VERSION/main -U --arch $guestarch \ | |
--allow-untrusted --root "$dest" \ | |
--initdb add alpine-base | |
mkdir -p "$dest"/{etc/apk,root} | |
# configure mirror | |
printf '%s/%s/main\n%s/%s/community\n' "$MIRROR" $VERSION "$MIRROR" $VERSION >"$dest"/etc/apk/repositories | |
for i in $(seq 0 10); do # https://github.com/systemd/systemd/issues/852 | |
echo "pts/$i" >>"$dest/etc/securetty" | |
done | |
# make console work | |
sed '/tty[0-9]:/ s/^/#/' -i "$dest"/etc/inittab | |
printf 'console::respawn:/sbin/getty 38400 console\n' >>"$dest"/etc/inittab | |
# minimal boot services | |
for s in hostname bootmisc syslog; do | |
ln -s /etc/init.d/$s "$dest"/etc/runlevels/boot/$s | |
done | |
for s in killprocs savecache; do | |
ln -s /etc/init.d/$s "$dest"/etc/runlevels/shutdown/$s | |
done | |
echo "" | |
echo "Alpine $VERSION $guestarch container was created successfully." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash -e | |
# Creates a systemd-nspawn container with Arch Linux | |
MIRROR=http://mirror.fra10.de.leaseweb.net/archlinux | |
ISO_DATE=latest | |
PKG_GROUPS="base" | |
wget_or_curl () { | |
if command -v wget >/dev/null; then | |
wget "$1" -O "$2" | |
elif command -v curl >/dev/null; then | |
curl -L "$1" -o "$2" | |
else | |
echo "missing either curl or wget" >&2 | |
return 1 | |
fi | |
} | |
if [ $UID -ne 0 ]; then | |
echo "run this script as root" >&2 | |
exit 1 | |
fi | |
dest="$1" | |
if [ -z "$dest" ]; then | |
echo "Usage: $0 <destination>" >&2 | |
exit 0 | |
fi | |
if [ -e "$dest/usr/bin" ]; then | |
echo "destination already seems to contain a root file system" >&2 | |
exit 1 | |
fi | |
[ "$(uname -m)" = x86_64 ] || { echo "unsupported architecture" >&2; exit 1; } | |
tarfile=$(mktemp) | |
trap 'rm $tarfile' EXIT | |
wget_or_curl "$MIRROR/iso/$ISO_DATE/archlinux-bootstrap-x86_64.tar.zst" $tarfile | |
mkdir -p "$dest" | |
tar -xaf $tarfile -C "$dest" --strip-components=1 --numeric-owner | |
# configure mirror | |
printf 'Server = %s/$repo/os/$arch\n' "$MIRROR" >"$dest"/etc/pacman.d/mirrorlist | |
sed '/^root:/ s|\*||' -i "$dest/etc/shadow" # passwordless login | |
rm "$dest/etc/resolv.conf" # systemd configures this | |
# https://github.com/systemd/systemd/issues/852 | |
[ -f "$dest/etc/securetty" ] && \ | |
printf 'pts/%d\n' $(seq 0 10) >>"$dest/etc/securetty" | |
# seems to be this bug https://github.com/systemd/systemd/issues/3611 | |
systemd-machine-id-setup --root="$dest" | |
# install the packages | |
systemd-nspawn -q -D "$dest" sh -c " | |
pacman-key --init && pacman-key --populate | |
pacman -Sy --noconfirm --needed ${PKG_GROUPS} | |
" | |
echo "" | |
echo "Arch Linux container was created successfully (bootstrapped from $ISO_DATE)" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash -e | |
# Creates a systemd-nspawn container with Ubuntu | |
CODENAME=${CODENAME:-noble} | |
wget_or_curl () { | |
if command -v wget >/dev/null; then | |
wget "$1" -O "$2" | |
elif command -v curl >/dev/null; then | |
curl -L "$1" -o "$2" | |
else | |
echo "missing either curl or wget" >&2 | |
return 1 | |
fi | |
} | |
if [ $UID -ne 0 ]; then | |
echo "run this script as root" >&2 | |
exit 1 | |
fi | |
dest="$1" | |
if [ -z "$dest" ]; then | |
echo "Usage: $0 <destination>" >&2 | |
exit 0 | |
fi | |
if [ -e "$dest/usr/bin" ]; then | |
echo "destination already seems to contain a root file system" >&2 | |
exit 1 | |
fi | |
if [ "$(uname -m)" = x86_64 ]; then | |
guestarch=amd64 | |
elif [ "$(uname -m)" = aarch64 ]; then | |
guestarch=arm64 | |
else | |
echo "unsupported architecture" >&2 | |
exit 1 | |
fi | |
rootfs=$(mktemp) | |
trap 'rm $rootfs' EXIT | |
wget_or_curl "http://cloud-images.ubuntu.com/${CODENAME}/current/${CODENAME}-server-cloudimg-${guestarch}-root.tar.xz" $rootfs | |
mkdir -p "$dest" | |
tar -xaf $rootfs -C "$dest" --numeric-owner | |
sed '/^root:/ s|\*||' -i "$dest/etc/shadow" # passwordless login | |
rm "$dest/etc/resolv.conf" # systemd configures this | |
# https://github.com/systemd/systemd/issues/852 | |
[ -f "$dest/etc/securetty" ] && \ | |
printf 'pts/%d\n' $(seq 0 10) >>"$dest/etc/securetty" | |
# container needs no mounts | |
>"$dest/etc/fstab" | |
# disable services and uninstall packages | |
systemd-nspawn -q -D "$dest" sh -c ' | |
[ -s /etc/environment ] && . /etc/environment | |
for unit in ssh.service ssh.socket systemd-timesyncd systemd-networkd-wait-online systemd-resolved; do | |
systemctl is-enabled "$unit" && systemctl disable "$unit" | |
done | |
apt-get -qq satisfy -y --purge "Conflicts: lxcfs, lxd, snapd, cloud-init" || \ | |
apt-get -qq purge --autoremove snapd lxcfs lxd cloud-init | |
' | |
echo "" | |
echo "Ubuntu $CODENAME $guestarch container was created successfully" |
Thank you, very useful scripts, saved a lot of time.
My fork of your script is here: https://github.com/mikhailnov/rootfs-scripts/blob/master/rootfs-ubuntu.sh
Starting alpine image on Ubuntu 20 gives
Sep 07 02:53:14 Gelato systemd-nspawn[79402]: getty: console: TIOCSCTTY: Operation not permitted
Sep 07 02:53:24 Gelato systemd-nspawn[79402]: getty: console: TIOCSCTTY: Operation not permitted
Sep 07 02:53:34 Gelato systemd-nspawn[79402]: getty: console: TIOCSCTTY: Operation not permitted
Sep 07 02:53:44 Gelato systemd-nspawn[79402]: getty: console: TIOCSCTTY: Operation not permitted
Sep 07 02:53:54 Gelato systemd-nspawn[79402]: getty: console: TIOCSCTTY: Operation not permitted
but
systemd-nspawn -M alpine -- /sbin/getty -nl /bin/ash 0 /dev/console
works.
printf 'console::respawn:/sbin/getty 38400 console\n' >>"$dest"/etc/inittab
does not seem to work.
Thank you so much for this! I hope you don't mind me using bits of your scripts here: https://github.com/X0RW3LL/XenSpawn
Linked to this gist in the references section <3
Thanks for this script! Building my own based on yours :)
Thanks for this script! Building my own based on yours :)
heh ;)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Awesome, thank you for sharing the script.
It's a little bit ugly but I change to use edge instead.