These are steps that I used on an Ubuntu 22.04 LTS (Jammy Jellyfish) 64-bit system to build an amd64 (64-bit) Debian (stable) live environment that can boot from CD or USB.

The live environment generated by this guide is bootable with legacy BIOS or modern EFI.

This guide exists for educational purposes. It is not necessarily the fastest guide or the best guide for your needs. There are many other apps, tutorials, walkthroughs, and methods that better accomplish what is in this guide.[1][2][3][4][5][6][7]. I hope this guide is helpful all the same.

The Debian Live Manual is more informative than this article could ever be.

Prerequisites

Install applications needed to build the live environment.

sudo apt-get install \
    debootstrap \
    squashfs-tools \
    xorriso \
    isolinux \
    syslinux-efi \
    grub-pc-bin \
    grub-efi-amd64-bin \
    grub-efi-ia32-bin \
    mtools \
    dosfstools

Create a directory where we will store all of the files we create throughout this guide.

mkdir -p "${HOME}/LIVE_BOOT"

Bootstrap and Configure Debian

Set up the base Debian environment. I am using stable for my distribution and amd64 for the architecture. Consult the list of debian mirrors.

Please change the URL in this command if there is a mirror that is closer to you.

sudo debootstrap \
    --arch=amd64 \
    --variant=minbase \
    stable \
    "${HOME}/LIVE_BOOT/chroot" \
    http://ftp.us.debian.org/debian/

Set a custom hostname for your Debian environment.

echo "debian-live" | sudo tee "${HOME}/LIVE_BOOT/chroot/etc/hostname"

Install a Linux kernel of your choosing. I chose the image linux-image-amd64. I also believe live-boot is a necessity. systemd-sys (or an equivalent) is also necessary to provide init.

sudo chroot "${HOME}/LIVE_BOOT/chroot" << EOF
apt-get update && \
apt-get install -y --no-install-recommends \
    linux-image-amd64 \
    live-boot \
    systemd-sysv
EOF

Read Debian’s ReduceDebian article for tips on reducing the size of your Debian environment if size is important and you want a minimal and compact installation. Please note that some live environments like Alpine Linux, Tiny Core Linux, and Puppy Linux are specifically optimized for a tiny footprint.

Although this article results in a relatively tiny live environment, generating an environment that is only a couple dozen MB large takes additional effort not covered in this article.

Install programs of your choosing, and then run apt-get clean to save some space. I use --no-install-recommends to avoid superfluous packages. You should decide what you need for your environment.

sudo chroot "${HOME}/LIVE_BOOT/chroot" << EOF
apt-get install -y --no-install-recommends \
    iwd \
    curl openssh-client \
    openbox xserver-xorg-core xserver-xorg xinit xterm \
    nano
EOF

Set the root password. root will be the only user in this live environment by default, but you may add additional users as needed.

sudo chroot "${HOME}/LIVE_BOOT/chroot" passwd root

Create directories that will contain files for our live environment files and scratch files.

mkdir -p "${HOME}/LIVE_BOOT"/{staging/{EFI/BOOT,boot/grub/x86_64-efi,isolinux,live},tmp}

Compress the chroot environment into a Squash filesystem.

sudo mksquashfs \
    "${HOME}/LIVE_BOOT/chroot" \
    "${HOME}/LIVE_BOOT/staging/live/filesystem.squashfs" \
    -e boot

Copy the kernel and initramfs from inside the chroot to the live directory.

cp "${HOME}/LIVE_BOOT/chroot/boot"/vmlinuz-* \
    "${HOME}/LIVE_BOOT/staging/live/vmlinuz" && \
cp "${HOME}/LIVE_BOOT/chroot/boot"/initrd.img-* \
    "${HOME}/LIVE_BOOT/staging/live/initrd"

Prepare Boot Loader Menus

Create an ISOLINUX (Syslinux) boot menu. This boot menu is used when booting in BIOS/legacy mode.

cat <<'EOF' > "${HOME}/LIVE_BOOT/staging/isolinux/isolinux.cfg"
UI vesamenu.c32

MENU TITLE Boot Menu
DEFAULT linux
TIMEOUT 600
MENU RESOLUTION 640 480
MENU COLOR border       30;44   #40ffffff #a0000000 std
MENU COLOR title        1;36;44 #9033ccff #a0000000 std
MENU COLOR sel          7;37;40 #e0ffffff #20ffffff all
MENU COLOR unsel        37;44   #50ffffff #a0000000 std
MENU COLOR help         37;40   #c0ffffff #a0000000 std
MENU COLOR timeout_msg  37;40   #80ffffff #00000000 std
MENU COLOR timeout      1;37;40 #c0ffffff #00000000 std
MENU COLOR msg07        37;40   #90ffffff #a0000000 std
MENU COLOR tabmsg       31;40   #30ffffff #00000000 std

LABEL linux
  MENU LABEL Debian Live [BIOS/ISOLINUX]
  MENU DEFAULT
  KERNEL /live/vmlinuz
  APPEND initrd=/live/initrd boot=live

LABEL linux
  MENU LABEL Debian Live [BIOS/ISOLINUX] (nomodeset)
  MENU DEFAULT
  KERNEL /live/vmlinuz
  APPEND initrd=/live/initrd boot=live nomodeset
EOF

Create a second, similar, boot menu for GRUB. This boot menu is used when booting in EFI/UEFI mode.

cat <<'EOF' > "${HOME}/LIVE_BOOT/staging/boot/grub/grub.cfg"
insmod part_gpt
insmod part_msdos
insmod fat
insmod iso9660

insmod all_video
insmod font

set default="0"
set timeout=30

# If X has issues finding screens, experiment with/without nomodeset.

menuentry "Debian Live [EFI/GRUB]" {
    search --no-floppy --set=root --label DEBLIVE
    linux ($root)/live/vmlinuz boot=live
    initrd ($root)/live/initrd
}

menuentry "Debian Live [EFI/GRUB] (nomodeset)" {
    search --no-floppy --set=root --label DEBLIVE
    linux ($root)/live/vmlinuz boot=live nomodeset
    initrd ($root)/live/initrd
}
EOF

Copy the grub.cfg file to the EFI BOOT directory.

cp "${HOME}/LIVE_BOOT/staging/boot/grub/grub.cfg" "${HOME}/LIVE_BOOT/staging/EFI/BOOT/"

Create a third boot config. This config will be an early configuration file that is embedded inside GRUB in the EFI partition. This finds the root and loads the GRUB config from there.

cat <<'EOF' > "${HOME}/LIVE_BOOT/tmp/grub-embed.cfg"
if ! [ -d "$cmdpath" ]; then
    # On some firmware, GRUB has a wrong cmdpath when booted from an optical disc.
    # https://gitlab.archlinux.org/archlinux/archiso/-/issues/183
    if regexp --set=1:isodevice '^(\([^)]+\))\/?[Ee][Ff][Ii]\/[Bb][Oo][Oo][Tt]\/?$' "$cmdpath"; then
        cmdpath="${isodevice}/EFI/BOOT"
    fi
fi
configfile "${cmdpath}/grub.cfg"
EOF

Your LIVE_BOOT directory should now roughly look like this.

LIVE_BOOT/chroot/*tons of chroot files*
LIVE_BOOT/staging/live/initrd
LIVE_BOOT/staging/live/vmlinuz
LIVE_BOOT/staging/live/filesystem.squashfs
LIVE_BOOT/staging/isolinux/isolinux.cfg
LIVE_BOOT/staging/EFI/BOOT
LIVE_BOOT/staging/boot/grub/grub.cfg
LIVE_BOOT/staging/boot/grub/x86_64-efi

Prepare Boot Loader Files

Copy BIOS/legacy boot required files into our workspace.

cp /usr/lib/ISOLINUX/isolinux.bin "${HOME}/LIVE_BOOT/staging/isolinux/" && \
cp /usr/lib/syslinux/modules/bios/* "${HOME}/LIVE_BOOT/staging/isolinux/"

Copy EFI/modern boot required files into our workspace.

cp -r /usr/lib/grub/x86_64-efi/* "${HOME}/LIVE_BOOT/staging/boot/grub/x86_64-efi/"

Generate an EFI bootable GRUB image.

grub-mkstandalone -O i386-efi \
    --modules="part_gpt part_msdos fat iso9660" \
    --locales="" \
    --themes="" \
    --fonts="" \
    --output="${HOME}/LIVE_BOOT/staging/EFI/BOOT/BOOTIA32.EFI" \
    "boot/grub/grub.cfg=${HOME}/LIVE_BOOT/tmp/grub-embed.cfg"
grub-mkstandalone -O x86_64-efi \
    --modules="part_gpt part_msdos fat iso9660" \
    --locales="" \
    --themes="" \
    --fonts="" \
    --output="${HOME}/LIVE_BOOT/staging/EFI/BOOT/BOOTx64.EFI" \
    "boot/grub/grub.cfg=${HOME}/LIVE_BOOT/tmp/grub-embed.cfg"

Create a FAT16 UEFI boot disk image containing the EFI bootloaders.

(cd "${HOME}/LIVE_BOOT/staging" && \
    dd if=/dev/zero of=efiboot.img bs=1M count=20 && \
    mkfs.vfat efiboot.img && \
    mmd -i efiboot.img ::/EFI ::/EFI/BOOT && \
    mcopy -vi efiboot.img \
        "${HOME}/LIVE_BOOT/staging/EFI/BOOT/BOOTIA32.EFI" \
        "${HOME}/LIVE_BOOT/staging/EFI/BOOT/BOOTx64.EFI" \
        "${HOME}/LIVE_BOOT/staging/boot/grub/grub.cfg" \
        ::/EFI/BOOT/
)

Create Bootable ISO/CD

The .iso file we create can be burned to a CD-ROM (optical media) and can also be written to a USB device with dd. The process here a bit complex, but that resultant behavior is common in many modern live environments such as the Ubuntu installation .iso file.

Please note that writing an .iso file to a USB device is not the same as installing the live environment directly to a USB device.

Generate the .iso disc image file.

xorriso \
    -as mkisofs \
    -iso-level 3 \
    -o "${HOME}/LIVE_BOOT/debian-custom.iso" \
    -full-iso9660-filenames \
    -volid "DEBLIVE" \
    --mbr-force-bootable -partition_offset 16 \
    -joliet -joliet-long -rational-rock \
    -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin \
    -eltorito-boot \
        isolinux/isolinux.bin \
        -no-emul-boot \
        -boot-load-size 4 \
        -boot-info-table \
        --eltorito-catalog isolinux/isolinux.cat \
    -eltorito-alt-boot \
        -e --interval:appended_partition_2:all:: \
        -no-emul-boot \
        -isohybrid-gpt-basdat \
    -append_partition 2 C12A7328-F81F-11D2-BA4B-00A0C93EC93B ${HOME}/LIVE_BOOT/staging/efiboot.img \
    "${HOME}/LIVE_BOOT/staging"

Citations