Created
          August 25, 2025 02:31 
        
      - 
      
- 
        Save Gurpartap/96cb7c6f6c4aa9c19bc5074a5dd7aee3 to your computer and use it in GitHub Desktop. 
    create an installimage-ready Debian 13 tarball
  
        
  
    
      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 | |
| ############################################################################### | |
| # build-trixie.sh – create an installimage-ready Debian 13 tarball | |
| ############################################################################### | |
| # Strict and predictable shell behavior | |
| set -euo pipefail | |
| # Found AUTOSETUP file '/autosetup' | |
| # Running unattended installimage installation ... | |
| # | |
| # DRIVE1 /dev/nvme0n1 | |
| # DRIVE2 /dev/nvme1n1 | |
| # SWRAID 1 | |
| # SWRAIDLEVEL 0 | |
| # HOSTNAME trixie | |
| # IPV4_ONLY no | |
| # USE_KERNEL_MODE_SETTING yes | |
| # PART /boot/efi esp 256M | |
| # PART /boot ext3 1024M | |
| # PART swap swap 32G | |
| # PART / ext4 320G | |
| # PART /srv ext4 all | |
| # IMAGE /root/Debian-1300-trixie-amd64-base.tar.gz | |
| # | |
| # | |
| # ALL DATA ON THE GIVEN DISKS WILL BE DESTROYED! | |
| # | |
| # DO YOU REALLY WANT TO CONTINUE? [y|N] y | |
| # | |
| # | |
| # WARNING: | |
| # Starting installation in 20 seconds ... | |
| # Press X to continue immediately ... | |
| # Installation will DELETE ALL DATA ON DISK(s)! | |
| # Press CTRL-C to abort now! | |
| # => ............. | |
| # ───── tweak here if you like ─────────────────────────────────────────────── | |
| export ROOT=/root/trixie-rootfs | |
| export IMAGE=/root/Debian-1300-trixie-amd64-base.tar.gz | |
| export HOSTNAME=trixie | |
| # ──────────────────────────────────────────────────────────────────────────── | |
| exec unshare --mount --pid --fork -- bash -c "$(cat <<'NSSCRIPT' | |
| set -eo pipefail | |
| mount --make-rprivate / # isolate our mounts | |
| # Bootstrap Debian 13 | |
| # Ensure target root is empty to avoid reusing stale trees | |
| if [ -d "$ROOT" ] && [ -n "$(ls -A "$ROOT" 2>/dev/null)" ]; then | |
| if [ "${CLEAN_ROOT:-0}" = "1" ]; then | |
| rm -rf "$ROOT" | |
| else | |
| echo "ERROR: ROOT directory '$ROOT' is not empty. Set CLEAN_ROOT=1 to remove it automatically, or choose a new ROOT path." >&2 | |
| exit 1 | |
| fi | |
| fi | |
| mkdir -p "$ROOT" | |
| debootstrap --arch=amd64 --components=main,contrib,non-free,non-free-firmware trixie "$ROOT" http://deb.debian.org/debian | |
| # Mount virtual filesystems (devpts fixes posix_openpt) | |
| mkdir -p "$ROOT"{/dev/pts,/run,/tmp} | |
| mount --bind /dev "$ROOT/dev" | |
| mount -t devpts devpts "$ROOT/dev/pts" | |
| mount -t proc none "$ROOT/proc" | |
| mount -t sysfs none "$ROOT/sys" | |
| mount -t tmpfs tmpfs "$ROOT/run" | |
| mount -t tmpfs tmpfs "$ROOT/tmp" | |
| chmod 755 "$ROOT/run" | |
| chmod 1777 "$ROOT/tmp" | |
| # Ensure DNS works inside chroot for apt operations | |
| install -D -m 644 /etc/resolv.conf "$ROOT/etc/resolv.conf" | |
| # Configure inside chroot | |
| chroot "$ROOT" env -i \ | |
| HOME=/root \ | |
| TERM=xterm-256color \ | |
| CHROOT_HOSTNAME="${HOSTNAME}" \ | |
| PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ | |
| /bin/bash -e <<CHROOT | |
| # Ensure strict mode inside chroot | |
| set -euo pipefail | |
| echo "${CHROOT_HOSTNAME:-localhost}" > /etc/hostname | |
| # Configure upstream-only apt sources BEFORE any installs (parity structure) | |
| cat > /etc/apt/sources.list << 'EOF' | |
| deb http://deb.debian.org/debian trixie main contrib non-free non-free-firmware | |
| # deb-src http://deb.debian.org/debian trixie main contrib non-free non-free-firmware | |
| deb http://deb.debian.org/debian trixie-updates main contrib non-free non-free-firmware | |
| # deb-src http://deb.debian.org/debian trixie-updates main contrib non-free non-free-firmware | |
| # deb http://deb.debian.org/debian trixie-backports main contrib non-free non-free-firmware | |
| # deb-src http://deb.debian.org/debian trixie-backports main contrib non-free non-free-firmware | |
| deb http://security.debian.org/debian-security trixie-security main contrib non-free non-free-firmware | |
| # deb-src http://security.debian.org/debian-security trixie-security main contrib non-free non-free-firmware | |
| EOF | |
| export DEBIAN_FRONTEND=noninteractive | |
| apt-get update | |
| # zstd first – avoids gzip fallback | |
| apt-get install --no-install-recommends -y zstd | |
| # locales | |
| apt-get install --no-install-recommends -y locales | |
| echo 'en_US.UTF-8 UTF-8' >> /etc/locale.gen | |
| locale-gen | |
| update-locale LANG=en_US.UTF-8 | |
| # pre-configure keyboard to avoid interactive prompts | |
| echo 'keyboard-configuration keyboard-configuration/layout select English (US)' | debconf-set-selections | |
| echo 'keyboard-configuration keyboard-configuration/variant select English (US)' | debconf-set-selections | |
| echo 'keyboard-configuration keyboard-configuration/model select Generic 105-key PC' | debconf-set-selections | |
| # essential packages (matching Hetzner's standard + trixie modern defaults) | |
| apt-get install --no-install-recommends -y \ | |
| linux-image-amd64 \ | |
| initramfs-tools \ | |
| initramfs-tools-bin \ | |
| initramfs-tools-core \ | |
| systemd-sysv \ | |
| grub-efi-amd64 \ | |
| grub-efi-amd64-bin \ | |
| grub-efi-amd64-signed \ | |
| grub-pc-bin \ | |
| shim-signed \ | |
| mdadm \ | |
| openssh-server \ | |
| dbus \ | |
| ifupdown \ | |
| net-tools \ | |
| iproute2 \ | |
| nftables \ | |
| unattended-upgrades \ | |
| ca-certificates \ | |
| gnupg \ | |
| curl \ | |
| wget \ | |
| sudo \ | |
| systemd-timesyncd \ | |
| isc-dhcp-client \ | |
| firmware-bnx2x \ | |
| firmware-misc-nonfree \ | |
| firmware-realtek \ | |
| intel-microcode \ | |
| amd64-microcode \ | |
| acl \ | |
| apt-utils \ | |
| at \ | |
| bash-completion \ | |
| bind9-dnsutils \ | |
| bind9-host \ | |
| bsdextrautils \ | |
| btrfs-progs \ | |
| busybox \ | |
| bzip2 \ | |
| console-setup \ | |
| console-setup-linux \ | |
| cryptsetup \ | |
| cryptsetup-bin \ | |
| cryptsetup-initramfs \ | |
| dosfstools \ | |
| efibootmgr \ | |
| ethtool \ | |
| file \ | |
| gdisk \ | |
| htop \ | |
| iptables \ | |
| keyboard-configuration \ | |
| lsb-release \ | |
| lsof \ | |
| lvm2 \ | |
| mailcap \ | |
| man-db \ | |
| manpages \ | |
| media-types \ | |
| mtr-tiny \ | |
| netcat-traditional \ | |
| openssl \ | |
| os-prober \ | |
| patch \ | |
| pci.ids \ | |
| pciutils \ | |
| perl \ | |
| python3 \ | |
| python3-apt \ | |
| python3-debian \ | |
| rsync \ | |
| traceroute \ | |
| util-linux-extra \ | |
| vim \ | |
| xfsprogs \ | |
| xz-utils | |
| # Strict verification of critical packages (fail fast) | |
| pkgs="linux-image-amd64 initramfs-tools initramfs-tools-bin grub-efi-amd64 grub-efi-amd64-bin grub-efi-amd64-signed shim-signed grub-pc-bin mdadm ifupdown openssh-server" | |
| for p in $pkgs; do | |
| if ! dpkg -s "$p" >/dev/null 2>&1; then | |
| echo "ERROR: required package '$p' not installed" >&2 | |
| exit 1 | |
| fi | |
| done | |
| # Protect critical packages from unintended removal during cleanup/triggers | |
| apt-mark manual linux-image-amd64 initramfs-tools initramfs-tools-core || true | |
| # Ensure SSH and networking come up on first boot | |
| systemctl enable ssh || true | |
| systemctl enable networking || true | |
| # installimage work-arounds and parity with Bookworm baseline | |
| # Align kernel image handling with baseline | |
| cat > /etc/kernel-img.conf << 'EOF' | |
| # Kernel image management overrides | |
| # See kernel-img.conf(5) for details | |
| do_symlinks = yes | |
| do_bootloader = no | |
| do_initrd = yes | |
| link_in_boot = no | |
| EOF | |
| # Ensure EFI mount exists for systems that need it (installimage will handle fstab) | |
| mkdir -p /boot/efi | |
| : > /etc/mdadm/mdadm.conf # placeholder | |
| # (moved earlier to ensure all installs use these sources) | |
| # create standard interfaces file (matching Hetzner's official images) | |
| cat > /etc/network/interfaces << 'EOF' | |
| # This file describes the network interfaces available on your system | |
| # and how to activate them. For more information, see interfaces(5) | |
| # Include files from /etc/network/interfaces.d: | |
| source /etc/network/interfaces.d/* | |
| # The loopback network interface | |
| auto lo | |
| iface lo inet loopback | |
| EOF | |
| # Bring packages current to latest point updates | |
| apt-get update | |
| apt-get -y upgrade | |
| # final image hygiene and clarity | |
| # - set GRUB defaults to match baseline | |
| cat > /etc/default/grub << 'EOF' | |
| # If you change this file, run 'update-grub' afterwards to update | |
| # /boot/grub/grub.cfg. | |
| # For full documentation of the options in this file, see: | |
| # info -f grub -n 'Simple configuration' | |
| GRUB_DEFAULT=0 | |
| GRUB_TIMEOUT=5 | |
| GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` | |
| GRUB_CMDLINE_LINUX_DEFAULT="quiet" | |
| GRUB_CMDLINE_LINUX="consoleblank=0 systemd.show_status=true" | |
| # Uncomment to disable graphical terminal | |
| GRUB_TERMINAL=console | |
| EOF | |
| # - ensure initramfs uses zstd compression (baseline) | |
| test -d /etc/initramfs-tools || { echo "ERROR: /etc/initramfs-tools missing (initramfs-tools not properly installed)" >&2; exit 1; } | |
| if grep -qE '^\s*COMPRESS=' /etc/initramfs-tools/initramfs.conf 2>/dev/null; then | |
| sed -i 's/^\s*COMPRESS=.*/COMPRESS=zstd/' /etc/initramfs-tools/initramfs.conf | |
| else | |
| printf '\nCOMPRESS=zstd\n' >> /etc/initramfs-tools/initramfs.conf | |
| fi | |
| # - mdadm baseline config | |
| cat > /etc/mdadm/mdadm.conf << 'EOF' | |
| # mdadm.conf | |
| # Please refer to mdadm.conf(5) for information about this file. | |
| HOMEHOST <system> | |
| MAILADDR root | |
| EOF | |
| # - enable persistent journal directory (journald uses it automatically) | |
| mkdir -p /var/log/journal | |
| # Generate SSH host keys at first boot if missing (idempotent) | |
| cat > /etc/systemd/system/ssh-hostkeys.service << 'EOF' | |
| [Unit] | |
| Description=Generate SSH host keys at first boot if missing | |
| Before=ssh.service | |
| [Service] | |
| Type=oneshot | |
| ExecStart=/usr/bin/ssh-keygen -A | |
| [Install] | |
| WantedBy=multi-user.target | |
| EOF | |
| systemctl enable ssh-hostkeys.service || true | |
| # - rebuild initramfs for all installed kernels and verify artifacts exist | |
| command -v update-initramfs >/dev/null 2>&1 || { echo "ERROR: update-initramfs not found" >&2; exit 1; } | |
| update-initramfs -u -k all | |
| ls /boot/vmlinuz-* >/dev/null 2>&1 || { echo "ERROR: kernel image missing in /boot (vmlinuz-*)" >&2; exit 1; } | |
| ls /boot/initrd.img-* >/dev/null 2>&1 || { echo "ERROR: initramfs image missing in /boot (initrd.img-*)" >&2; exit 1; } | |
| # - empty resolv.conf to avoid leaking build-host resolvers; installimage will write it | |
| : > /etc/resolv.conf | |
| # - ensure no pre-generated SSH host keys are shipped; systemd/sshd-keygen will create on first boot | |
| rm -f /etc/ssh/ssh_host_* || true | |
| # - ensure a fresh machine-id is created on first boot | |
| truncate -s 0 /etc/machine-id || true | |
| ln -sf /etc/machine-id /var/lib/dbus/machine-id || true | |
| # - remove random-seed to avoid entropy reuse | |
| rm -f /var/lib/systemd/random-seed || true | |
| # Note: Avoid purging initramfs/dracut/dhcpcd here; removing them can cascade | |
| # and cause the kernel/initramfs to be removed by apt. Keep image neutral. | |
| # - trim logs and temp files to keep image small and pristine | |
| find /var/log -type f -exec truncate -s 0 {} + || true | |
| rm -rf /var/tmp/* || true | |
| apt-get clean | |
| rm -rf /var/lib/apt/lists/* | |
| # Final sanity: verify boot artifacts still present | |
| ls /boot/vmlinuz-* >/dev/null 2>&1 || { echo "ERROR: kernel image missing in /boot (vmlinuz-*)" >&2; exit 1; } | |
| ls /boot/initrd.img-* >/dev/null 2>&1 || { echo "ERROR: initramfs image missing in /boot (initrd.img-*)" >&2; exit 1; } | |
| CHROOT | |
| # Unmount (namespace-local) | |
| umount -l "$ROOT"/{proc,sys,dev/pts,dev,run,tmp} | |
| # Create the tarball | |
| tar --numeric-owner -c -z -p \ | |
| -f "$IMAGE" \ | |
| -C "$ROOT" \ | |
| --exclude=dev \ | |
| --exclude=proc \ | |
| --exclude=sys \ | |
| --exclude=run/* \ | |
| --exclude=tmp/* \ | |
| . | |
| echo "==== Tarball created at: $IMAGE ====" | |
| NSSCRIPT | |
| )" | 
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment