Create a Custom Debian Live Environment (CD or USB)
These are steps that I used on an Ubuntu 18.04 LTS (Bionic Beaver) 64-bit system to build an x86 Debian 9 (Stretch) live environment that I can boot from CD or USB.
These steps can be used to create a live environment that is BIOS bootable (MBR), UEFI bootable (GPT), or combination of both UEFI and BIOS bootable. Something unique about this guide is that Syslinux/Isolinux are not used. Only Grub boot equipment. This is done for consistency and to avoid mixing the two (Syslinux/Isolinux alone cannot accomplish everything covered in this article, but Grub can).
Here are some alternatives to my guide that may be better solutions for anyone reading this article: live-build, mkusb, UNetbootin, xixer, rufus, YUMI, Simple-cdd. You should also look at the Debian DebianCustomCD documentation as it will prove more informative than this article could ever be.
I wrote this guide more for educational purposes than anything. It is not necessarily the fastest guide or the best guide for your needs. I hope it is helpful all the same.
Warning: I have highlighted all the places you should be in the [chroot] environment. Be careful! Running some of these commands on your local environment instead of in the chroot can damage your system.
Prerequisites
Install applications we need to build the environment.
sudo apt-get install \
debootstrap \
squashfs-tools \
xorriso \
grub-pc-bin \
grub-efi-amd64-bin \
mtools
Create a directory where we will store all of the files we create throughout this guide.
mkdir $HOME/LIVE_BOOT
Bootstrap and Configure Debian
Set up the base Debian environment. I am using stretch
for my distribution and i386
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=i386 \
--variant=minbase \
stretch \
$HOME/LIVE_BOOT/chroot \
http://ftp.us.debian.org/debian/
Chroot to the Debian environment we just bootstrapped.
sudo chroot $HOME/LIVE_BOOT/chroot
[chroot] Set a custom hostname for your Debian environment.
echo "debian-live" > /etc/hostname
[chroot] Figure out which Linux Kernel you want in the live environment.
apt-cache search linux-image
[chroot] I chose the image linux-image-686
. I also believe live-boot
is a necessity. systemd-sys
(or an equivalent) is also necessary to provide init.
apt-get update && \
apt-get install --no-install-recommends \
linux-image-686 \
live-boot \
systemd-sysv
[chroot] 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.
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 Tiny Core Linux or 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.
apt-get install --no-install-recommends \
network-manager net-tools wireless-tools wpagui \
curl openssh-client \
blackbox xserver-xorg-core xserver-xorg xinit xterm \
nano && \
apt-get clean
[chroot] Set the root password. root
will be the only user in this live environment by default, but you may add additional users as needed.
passwd root
[chroot] Exit the chroot.
exit
Create directories that will contain files for our live environment files and scratch files.
mkdir -p $HOME/LIVE_BOOT/{scratch,image/live}
Compress the chroot environment into a Squash filesystem.
sudo mksquashfs \
$HOME/LIVE_BOOT/chroot \
$HOME/LIVE_BOOT/image/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/image/vmlinuz && \
cp $HOME/LIVE_BOOT/chroot/boot/initrd.img-* \
$HOME/LIVE_BOOT/image/initrd
Create a menu configuration file for grub. Please note that the insmod all_video
line was needed in my testing to deal with a bug in UEFI booting for one of my machines. Perhaps not everyone will need that line, but I did.
This config instructs Grub to use the search
command to infer which device contains our live environment. This seems like the most portable solution considering the various ways we may write our live environment to bootable media.
cat <<'EOF' >$HOME/LIVE_BOOT/scratch/grub.cfg
search --set=root --file /DEBIAN_CUSTOM
insmod all_video
set default="0"
set timeout=30
menuentry "Debian Live" {
linux /vmlinuz boot=live quiet nomodeset
initrd /initrd
}
EOF
Create a special file in image
named DEBIAN_CUSTOM
. This file will be used to help Grub
figure out which device contains our live filesystem. This file name must be unique and must match the file name in our grub.cfg
config.
touch $HOME/LIVE_BOOT/image/DEBIAN_CUSTOM
Your LIVE_BOOT
directory should now roughly look like this.
LIVE_BOOT/chroot/*tons of chroot files*
LIVE_BOOT/scratch/grub.cfg
LIVE_BOOT/image/DEBIAN_CUSTOM
LIVE_BOOT/image/initrd
LIVE_BOOT/image/vmlinuz
LIVE_BOOT/image/live/filesystem.squashfs
Create a bootable medium
Please note that there are two separate sets of instructions below for creating a bootable medium for the live environment. One process is titled Create Bootable ISO/CD and the other, separate process, is titled Create Bootable USB.
- The Create Bootable ISO/CD instructions will result in an
.iso
image file containing our live environment. - The Create Bootable USB instructions will result in our live environment being installed directly to a USB device.
The .iso
file we create with the Create Bootable ISO/CD instructions can be burned to a CD-ROM (optical media), or written to a USB device with dd
. The functionality that allows this “dd-able” behavior from our .iso
file does not come for free. The process is 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. Read more in my Notes which details my discoveries.
Create Bootable ISO/CD
Install the live environment to an .iso
file which can be burned to optical media.
As stated above, the .iso
file generated by these steps can be written to a USB device with something like dd
.
I have separated the instructions for creating a BIOS bootable, UEFI bootable, or combination BIOS + UEFI bootable .iso
in order to illustrate the separate processes. Click either “BIOS”, “UEFI”, or “BIOS + UEFI” and follow the instructions that are appropriate for your needs.
Create a grub BIOS image.
grub-mkstandalone \
--format=i386-pc \
--output=$HOME/LIVE_BOOT/scratch/core.img \
--install-modules="linux normal iso9660 biosdisk memdisk search tar ls" \
--modules="linux normal iso9660 biosdisk search" \
--locales="" \
--fonts="" \
"boot/grub/grub.cfg=$HOME/LIVE_BOOT/scratch/grub.cfg"
Use cat
to combine a bootable Grub cdboot.img
bootloader with our boot image.
cat \
/usr/lib/grub/i386-pc/cdboot.img \
$HOME/LIVE_BOOT/scratch/core.img \
> $HOME/LIVE_BOOT/scratch/bios.img
Generate the ISO file.
xorriso \
-as mkisofs \
-iso-level 3 \
-full-iso9660-filenames \
-volid "DEBIAN_CUSTOM" \
--grub2-boot-info \
--grub2-mbr /usr/lib/grub/i386-pc/boot_hybrid.img \
-eltorito-boot \
boot/grub/bios.img \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
--eltorito-catalog boot/grub/boot.cat \
-output "${HOME}/LIVE_BOOT/debian-custom.iso" \
-graft-points \
"${HOME}/LIVE_BOOT/image" \
/boot/grub/bios.img=$HOME/LIVE_BOOT/scratch/bios.img
Now burn the ISO to a CD and you should be ready to boot from it using a BIOS system.
Create a grub UEFI image.
grub-mkstandalone \
--format=x86_64-efi \
--output=$HOME/LIVE_BOOT/scratch/bootx64.efi \
--locales="" \
--fonts="" \
"boot/grub/grub.cfg=$HOME/LIVE_BOOT/scratch/grub.cfg"
Create a FAT16 UEFI boot disk image containing the EFI bootloader. Note the use of the mmd
and mcopy
commands to copy our UEFI boot loader named bootx64.efi
.
(cd $HOME/LIVE_BOOT/scratch && \
dd if=/dev/zero of=efiboot.img bs=1M count=10 && \
mkfs.vfat efiboot.img && \
mmd -i efiboot.img efi efi/boot && \
mcopy -i efiboot.img ./bootx64.efi ::efi/boot/
)
Generate the ISO file.
xorriso \
-as mkisofs \
-iso-level 3 \
-full-iso9660-filenames \
-volid "DEBIAN_CUSTOM" \
-eltorito-alt-boot \
-e EFI/efiboot.img \
-no-emul-boot \
-append_partition 2 0xef ${HOME}/LIVE_BOOT/scratch/efiboot.img \
-output "${HOME}/LIVE_BOOT/debian-custom.iso" \
-graft-points \
"${HOME}/LIVE_BOOT/image" \
/EFI/efiboot.img=$HOME/LIVE_BOOT/scratch/efiboot.img
Now burn the ISO to a CD and you should be ready to boot from it using a UEFI system.
Create a grub UEFI image.
grub-mkstandalone \
--format=x86_64-efi \
--output=$HOME/LIVE_BOOT/scratch/bootx64.efi \
--locales="" \
--fonts="" \
"boot/grub/grub.cfg=$HOME/LIVE_BOOT/scratch/grub.cfg"
Create a FAT16 UEFI boot disk image containing the EFI bootloader. Note the use of the mmd
and mcopy
commands to copy our UEFI boot loader named bootx64.efi
.
(cd $HOME/LIVE_BOOT/scratch && \
dd if=/dev/zero of=efiboot.img bs=1M count=10 && \
mkfs.vfat efiboot.img && \
mmd -i efiboot.img efi efi/boot && \
mcopy -i efiboot.img ./bootx64.efi ::efi/boot/
)
Create a grub BIOS image.
grub-mkstandalone \
--format=i386-pc \
--output=$HOME/LIVE_BOOT/scratch/core.img \
--install-modules="linux normal iso9660 biosdisk memdisk search tar ls" \
--modules="linux normal iso9660 biosdisk search" \
--locales="" \
--fonts="" \
"boot/grub/grub.cfg=$HOME/LIVE_BOOT/scratch/grub.cfg"
Use cat
to combine a bootable Grub cdboot.img
bootloader with our boot image.
cat \
/usr/lib/grub/i386-pc/cdboot.img \
$HOME/LIVE_BOOT/scratch/core.img \
> $HOME/LIVE_BOOT/scratch/bios.img
Generate the ISO file.
xorriso \
-as mkisofs \
-iso-level 3 \
-full-iso9660-filenames \
-volid "DEBIAN_CUSTOM" \
-eltorito-boot \
boot/grub/bios.img \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
--eltorito-catalog boot/grub/boot.cat \
--grub2-boot-info \
--grub2-mbr /usr/lib/grub/i386-pc/boot_hybrid.img \
-eltorito-alt-boot \
-e EFI/efiboot.img \
-no-emul-boot \
-append_partition 2 0xef ${HOME}/LIVE_BOOT/scratch/efiboot.img \
-output "${HOME}/LIVE_BOOT/debian-custom.iso" \
-graft-points \
"${HOME}/LIVE_BOOT/image" \
/boot/grub/bios.img=$HOME/LIVE_BOOT/scratch/bios.img \
/EFI/efiboot.img=$HOME/LIVE_BOOT/scratch/efiboot.img
Now burn the ISO to a CD and you should be ready to boot from it using either a UEFI or a BIOS system.
Create Bootable USB
Install the live environment to a USB device.
As stated above, installing a live environment to a USB device is not the same as writing an .iso
file to a USB device. The end result is the same for the most part in both those scenarios, but there are subtle differences worth understanding, and there are valid reasons someone may want to install a live environment directly to a USB device rather than writing an .iso
file to a USB device.
I have separated the instructions for creating a BIOS bootable, UEFI bootable, or combination BIOS + UEFI bootable USB device in order to illustrate the separate processes. Click either “BIOS”, “UEFI”, or “BIOS + UEFI” and follow the instructions that are appropriate for your needs.
I am assuming you have an umounted blank USB drive at /dev/sdz. To allow easily swapping in a real block device, I am using a variable named $disk
in these commands.
Export the disk
variable.
export disk=/dev/sdz
Create a mount point for the USB drive.
sudo mkdir -p /mnt/usb
Partition the USB drive using parted
. This command creates 1 partition in a traditional MBR layout.
sudo parted --script $disk \
mklabel msdos \
mkpart primary fat32 1MiB 100%
Format the partition.
sudo mkfs.vfat -F32 ${disk}1
Mount the partition.
sudo mount ${disk}1 /mnt/usb
Install Grub for i386-pc booting.
sudo grub-install \
--target=i386-pc \
--boot-directory=/mnt/usb/boot \
--recheck \
$disk
Create a live
directory on the USB device.
sudo mkdir -p /mnt/usb/{boot/grub,live}
Copy the Debian live environment files we previously generated to the USB disk.
sudo cp -r $HOME/LIVE_BOOT/image/* /mnt/usb/
Copy the grub.cfg
boot configuration to the USB device.
sudo cp \
$HOME/LIVE_BOOT/scratch/grub.cfg \
/mnt/usb/boot/grub/grub.cfg
Now unmount the disk and you should be ready to boot from it on a BIOS system.
sudo umount /mnt/usb
I am assuming you have an umounted blank USB drive at /dev/sdz. To allow easily swapping in a real block device, I am using a variable named $disk
in these commands.
Export the disk
variable.
export disk=/dev/sdz
Create some mount points for the USB drive.
sudo mkdir -p /mnt/{usb,efi}
Partition the USB drive using parted
. This command creates 2 partitions in a GPT (Guid Partition Table) layout. One partition for UEFI, and one for our Debian OS and other live data.
sudo parted --script $disk \
mklabel gpt \
mkpart ESP fat32 1MiB 200MiB \
name 1 EFI \
set 1 esp on \
mkpart primary fat32 200MiB 100% \
name 2 LINUX \
set 2 msftdata on
Format the UEFI and data partitions.
sudo mkfs.vfat -F32 ${disk}1 && \
sudo mkfs.vfat -F32 ${disk}2
Mount the partitions.
sudo mount ${disk}1 /mnt/efi && \
sudo mount ${disk}2 /mnt/usb
Install Grub for x86_64 UEFI booting.
sudo grub-install \
--target=x86_64-efi \
--efi-directory=/mnt/efi \
--boot-directory=/mnt/usb/boot \
--removable \
--recheck
Create a live
directory on the USB device.
sudo mkdir -p /mnt/usb/{boot/grub,live}
Copy the Debian live environment files we previously generated to the USB disk.
sudo cp -r $HOME/LIVE_BOOT/image/* /mnt/usb/
Copy the grub.cfg
boot configuration to the USB device.
sudo cp \
$HOME/LIVE_BOOT/scratch/grub.cfg \
/mnt/usb/boot/grub/grub.cfg
Now unmount the disk and you should be ready to boot from it on a UEFI system.
sudo umount /mnt/{usb,efi}
I am assuming you have an umounted blank USB drive at /dev/sdz. To allow easily swapping in a real block device, I am using a variable named $disk
in these commands.
Export the disk
variable.
export disk=/dev/sdz
Create some mount points for the USB drive.
sudo mkdir -p /mnt/{usb,efi}
Partition the USB drive using parted
. This command creates 3 partitions in a GPT (Guid Partition Table) layout. One partition for the BIOS boot record, one for UEFI, and one for our Debian OS and other live data.
sudo parted --script $disk \
mklabel gpt \
mkpart primary fat32 2048s 4095s \
name 1 BIOS \
set 1 bios_grub on \
mkpart ESP fat32 4096s 413695s \
name 2 EFI \
set 2 esp on \
mkpart primary fat32 413696s 100% \
name 3 LINUX \
set 3 msftdata on
Generate a hybrid MBR for the USB device. Note, this is non-standard and so may not work on all systems. The only guides I’ve found on hybrid MBRs show that it must be done with gdisk
. gdisk
supports commands not in sgdisk
, so this command is not easily scriptable. The documentation for gdisk
warn that this procedure is non-standard, flaky, and unsupported, but this does generally seem to do what is expected. It allows for both BIOS and UEFI booting from the same USB device.
sudo gdisk $disk << EOF
r # recovery and transformation options
h # make hybrid MBR
1 2 3 # partition numbers for hybrid MBR
N # do not place EFI GPT (0xEE) partition first in MBR
EF # MBR hex code
N # do not set bootable flag
EF # MBR hex code
N # do not set bootable flag
83 # MBR hex code
Y # set the bootable flag
x # extra functionality menu
h # recompute CHS values in protective/hybrid MBR
w # write table to disk and exit
Y # confirm changes
EOF
Format the UEFI and data partitions.
sudo mkfs.vfat -F32 ${disk}2 && \
sudo mkfs.vfat -F32 ${disk}3
Mount the partitions.
sudo mount ${disk}2 /mnt/efi && \
sudo mount ${disk}3 /mnt/usb
Install Grub for x86_64 UEFI booting.
sudo grub-install \
--target=x86_64-efi \
--efi-directory=/mnt/efi \
--boot-directory=/mnt/usb/boot \
--removable \
--recheck
Install Grub for i386-pc booting.
sudo grub-install \
--target=i386-pc \
--boot-directory=/mnt/usb/boot \
--recheck \
$disk
Create a live
directory on the USB device.
sudo mkdir -p /mnt/usb/{boot/grub,live}
Copy the Debian live environment files we previously generated to the USB disk.
sudo cp -r $HOME/LIVE_BOOT/image/* /mnt/usb/
Copy the grub.cfg
boot configuration to the USB device.
sudo cp \
$HOME/LIVE_BOOT/scratch/grub.cfg \
/mnt/usb/boot/grub/grub.cfg
Now unmount the disk and you should be ready to boot from it on either a BIOS or UEFI system.
sudo umount /mnt/{usb,efi}
Notes + −
Please note that the result of writing an .iso
file generated by the ISO/CD instructions to a USB device is not the same as the other set of instructions for Direct USB.
If you write an .iso
to a USB device you are deleting all the data (including partitions/filesystem) on that USB device and replacing them with a read-only iso9660
filesystem, which is normally what we see on CDs! On the other hand, if we use the Direct USB instructions, then the USB device will have traditional partitions that we can read from and write to on any standard machine.
Please read in entirety the syslinux isohybrid MBR article and the El-Torito article on Hybrid ISO booting and their related articles. The Debian xorriso man page has excellent documentation around ISO/USB booting as well.
Syslinux/Isolinux are more
commonly
used when creating an .iso
that can also be written directly to USB, but the Debian xorrisofs docs say the following.
EFI boot equipment may be combined with above ISOLINUX isohybrid for PC-BIOS in a not really UEFI-2.4 compliant way…
I don’t like that not really...compliant
bit.
Though, the docs also point out that it does work well. However, I’m trying to consolidate on Grub to avoid a mix of Syslinux configs/files and Grub commands/files.
Figuring out the right syntax and bootloaders to use for each command was a nightmare, and took a lot of time and reading and trial and error. I realize there are simpler commands like grub-mkrescue
, but I wanted to explicitly call out the manual commands used for each process.
The GNU xorrisofs docs seem outdated. The Debian docs were more reliable with better examples.
Examining the grub-mkrescue source code and this xorriso mailing list exchange helped me figure out how to use the --grub2-mbr
flag to install the standard MBR bootloader in the .iso
file.
Supposedly, we can also use -partition_offset
so the .iso
can be written to USB to help for natural disk layouts on the USB device, but I could not figure out how to get that working.
… facilitates later manipulations of the USB stick by tools for partitioning and formatting…
This blurb is confusing, but I realized it means Grub
can be used as the boot equipment for a CD in the same way we would use Syslinux with isohybrid MBR entries.
More compliant with UEFI-2.4 is to decide for either MBR or GPT and to append a copy of the EFI System Partition in order to avoid overlap of ISO partition and EFI partition. Here for MBR:
-eltorito-alt-boot -e 'boot/grub/efi.img' -no-emul-boot \ -append_partition 2 0xef ./CD_root/boot/grub/efi.img \
The resulting ISOs are supposed to boot from optical media and USB stick. One may omit option -eltorito-alt-boot if no option -b is used to make the ISO bootable via PC-BIOS.
That worked wonderfully for me to get a pure Grub result.
This is a super handy command for examining the commands that may have been used to generate an .iso
file.
xorriso -hfsplus on -indev IMAGE.iso \
-report_el_torito plain -report_system_area plain \
-print "" -print "======= Proposal for xorrisofs options:" \
-report_el_torito as_mkisofs
Figuring out which modules are needed for what behavior is frustrating. There are docs, but they do not seem to easily correlate to “this is required to list devices with ls
” and similar behavior. I eventually found the magic combo for grub-mkstandalone
, which cuts down a few steps. Here’s how to do it with grub-mkimage
. More verbose, but easier to debug.
Create a Grub early config file. Note that the label DEBIAN_CUSTOM
matters. We must use the same label on our .iso
so that Grub always knows where to find our boot media.
cat <<'EOF' >$HOME/LIVE_BOOT/scratch/embedded.cfg
search --no-floppy --set=root --label DEBIAN_CUSTOM
EOF
Generate a memdisk ourselves.
tar \
-C $HOME/LIVE_BOOT/scratch \
--transform 's;\.;boot/grub;' \
-cf $HOME/LIVE_BOOT/scratch/memdisk.tar \
./grub.cfg
Bring it all together.
grub-mkimage \
--directory=/usr/lib/grub/i386-pc \
--format=i386-pc \
--prefix="/boot/grub" \
--output=$HOME/LIVE_BOOT/scratch/core.img \
--memdisk=$HOME/LIVE_BOOT/scratch/memdisk.tar \
--config=$HOME/LIVE_BOOT/scratch/embedded.cfg \
linux normal iso9660 biosdisk memdisk search tar
Citations
- sgdisk(8) - Linux man page
- Hybrid UEFI GPT + BIOS GPT/MBR boot
- “No suitable mode found” error
- Where is the memtest option on the Ubuntu 64-bit live CD?
- Remastering the Install ISO
- Install GRUB2 in (U)EFI systems
- 6.4 Embedding a configuration file into GRUB
- Standalone Grub2 EFI installation - grub.cfg placement?
- actual usage of ‘grub-mkimage --config= ‘
- GRUB2 How To (4): memdisk and loopback device
- How does the grub efi loader find the correct grub.cfg and boot directory?
- How to Rescue a Non-booting GRUB 2 on Linux
- Using a CD
- Boot Linux with extlinux from EFI & GPT
- GRUB2 Modules
- grub-install.c
- Grub2/Troubleshooting
- Set up PreLoader
- Using PreLoader
- build.sh
- Archiso
- The simple menu system
- UEFI
- Make UEFI bootable live CD
- What is the proper way to use ISOLINUX with UEFI?
- Booting from removable media
- UEFI systems
- Managing EFI Boot Loaders for Linux: EFI Boot Loader Installation
- Examples
- Grub2 El-Torito CD
- Create ISO image with GRUB2
- DebianLive MultibootISO
- 11 GRUB image files
- stage2_eltorito missing
- 3.4 Making a GRUB bootable CD-ROM
- why is grub2 ignoring kernel options when boot from el torito on CD?
- Boot UEFI does not work from CD/DVD
- UEFI problem after upgrading to VMWS player 12
- make-efi
- UEFI + BIOS bootable live Debian stretch amd64 with persistence
- [syslinux] Isohybrid wiki page and UEFI