Last active
December 26, 2025 21:04
-
-
Save olafdietsche/d0fd494d6f8f63e5fb0d9740ae16ee0d to your computer and use it in GitHub Desktop.
Create a minimal debian image
This file contains hidden or 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 | |
| # | |
| # usage: TMPDIR=/path/to/tmp bash $0 <image-path> <debian-release> [<debian-mirror>] | |
| # sudo TMPDIR=/path/to/tmp bash build-image.sh /path/to/file.img trixie "file:///path/to/mirror-debian" | |
| # sudo TMPDIR=/path/to/tmp bash build-image.sh /dev/sdx trixie | |
| usage() | |
| { | |
| echo "usage: $0 <image-file/block-device> <debian-release> <debian-mirror>" >&2 | |
| exit 2 | |
| } | |
| if test $# -lt 1 -o $# -gt 3; then | |
| usage | |
| fi | |
| set -x | |
| set -e | |
| PS4='+ [$(date +"%Y-%m-%d %H:%M:%S.%N")] ${BASH_SOURCE}(${LINENO}) rc=$? ' | |
| cleanup() | |
| { | |
| set +e | |
| mountpoint --quiet "${rootdir}/proc" && umount "${rootdir}/proc" | |
| mountpoint --quiet "${rootdir}/sys/firmware/efi/efivars" && umount "${rootdir}/sys/firmware/efi/efivars" | |
| mountpoint --quiet "${rootdir}/sys" && umount "${rootdir}/sys" | |
| mountpoint --quiet "${rootdir}/dev/pts" && umount "${rootdir}/dev/pts" | |
| mountpoint --quiet "${rootdir}/dev" && umount "${rootdir}/dev" | |
| mountpoint --quiet "${rootdir}/boot/efi" && umount "${rootdir}/boot/efi" | |
| mountpoint --quiet "${rootdir}" && umount "${rootdir}" | |
| partx --delete "${partition_efi}" | |
| partx --delete "${partition_lx}" | |
| losetup --detach "${block_device}" | |
| rmdir "${rootdir}" | |
| } | |
| image_path="$1" | |
| rootdir="$(mktemp -d)" | |
| trap "cleanup" exit | |
| debian_release="$2" | |
| debian_mirror="$3" | |
| # create partitions | |
| sfdisk "${image_path}" <<EOF | |
| label: dos | |
| type=uefi size=128m | |
| type=linux | |
| EOF | |
| # setup block device | |
| if test -f "${image_path}"; then | |
| block_device=$(losetup --show --partscan --find "${image_path}") | |
| elif test -b "${image_path}"; then | |
| block_device="${image_path}" | |
| else | |
| echo "${image_path}: not a regular file or block device" >&2 | |
| usage | |
| fi | |
| { | |
| read -r partition_efi | |
| read -r partition_lx | |
| } < <(lsblk --noheadings --output=type,path --sort=path "${block_device}" | awk '$1 == "part" {print $2;}') | |
| mkfs -t vfat -F 32 "${partition_efi}" | |
| mkfs -t ext2 -T big "${partition_lx}" | |
| mount "${partition_lx}" "${rootdir}" | |
| mkdir -p "${rootdir}/boot/efi" | |
| mount "${partition_efi}" "${rootdir}/boot/efi" | |
| base_packages="linux-image-amd64,grub-efi,locales,console-setup,zstd,iputils-ping,iproute2,isc-dhcp-client,less,sudo,lsof" | |
| systemd_packages="dbus,systemd-resolved,systemd-sysv" | |
| init_packages="${systemd_packages}" | |
| sysvinit_packages="sysvinit-core,systemd-standalone-sysusers" | |
| # init_packages="${sysvinit_packages}" | |
| system_packages="mdadm,cryptsetup,smartmontools,fdisk,lvm2,e2fsprogs,udev" | |
| server_packages="openssh-server,openssh-sftp-server,cifs-utils,rsync" | |
| all_packages="${base_packages},${init_packages},${system_packages},${server_packages}" | |
| debootstrap --variant=minbase --include="${all_packages}" "${debian_release}" "${rootdir}" ${debian_mirror} | |
| mountpoint --quiet "${rootdir}/proc" || mount -t proc none "${rootdir}/proc" | |
| mountpoint --quiet "${rootdir}/sys" || mount -t sysfs none "${rootdir}/sys" | |
| mountpoint --quiet "${rootdir}/dev" || mount -t devtmpfs none "${rootdir}/dev" | |
| mountpoint --quiet "${rootdir}/dev/pts" || mount -t devpts none "${rootdir}/dev/pts" | |
| timezone="Europe/Berlin" | |
| rm -f "${rootdir}/etc/localtime"; ln -s "/usr/share/zoneinfo/${timezone}" "${rootdir}/etc/localtime" | |
| echo "${timezone}" >"${rootdir}/etc/timezone" | |
| cat >>"${rootdir}/etc/initramfs-tools/initramfs.conf" <<EOF | |
| # | |
| # KEYMAP: [ y | n ] | |
| # | |
| # Load a keymap during the initramfs stage. | |
| # | |
| KEYMAP=y | |
| EOF | |
| cat >"${rootdir}/etc/default/keyboard" <<EOF | |
| XKBMODEL="pc105" | |
| XKBLAYOUT="de" | |
| XKBVARIANT="nodeadkeys" | |
| XKBOPTIONS="" | |
| EOF | |
| cat >"${rootdir}/etc/systemd/network/lan.network" <<EOF | |
| [Match] | |
| Name=en* | |
| [Network] | |
| DHCP=yes | |
| EOF | |
| echo host01 >"${rootdir}/etc/hostname" | |
| # passwd root | |
| # password=debian | |
| # openssl passwd -5 debian | |
| hashed_password='$5$3z53yQfW5Y0BMzTt$O8UD2LG.hx6HG.ory05S/k8wrBwc6TPDdVagRJE1hN1' | |
| usermod --root "${rootdir}" --password "${hashed_password}" root | |
| # adduser olaf | |
| # password=olaf | |
| # openssl passwd -5 olaf | |
| hashed_password='$5$CIC5d4a1IsXfW59W$vApq7IsiWYeJnPN/lp025cYBE/alDqG1A91trATdJjB' | |
| useradd --root "${rootdir}" --password "${hashed_password}" --create-home --user-group --groups sudo olaf | |
| # fetch uuid of root partition | |
| uuid=$(lsblk --noheadings --output mountpoint,uuid | awk '$1 == "'"${rootdir}"'" {print $2;}') | |
| # make /var (root) writable | |
| echo "UUID=${uuid} / ext2 errors=remount-ro,rw 0 1" >>"${rootdir}/etc/fstab" | |
| # inside chroot | |
| cat >"${rootdir}/tmp/setup.sh" <<EOF | |
| grub-install --target x86_64-efi --removable "${block_device}" | |
| grub-mkconfig -o /boot/grub/grub.cfg | |
| locale-gen de_DE.UTF-8 | |
| locale-gen en_US.UTF-8 | |
| systemctl enable systemd-networkd.service systemd-resolved.service | |
| update-initramfs -u | |
| apt-get clean | |
| rm /var/lib/apt/lists/*_Release /var/lib/apt/lists/*_Packages | |
| rm /var/cache/debconf/* | |
| EOF | |
| chroot "${rootdir}" sh -x /tmp/setup.sh | |
| rm -f "${rootdir}/tmp/setup.sh" | |
| # eof chroot |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment