Multiboot USB Stick with Persistence from the Command Line

There exist numerous graphical tools for creating bootable USB drives. Some support creating USB drives that can boot into any number of Linux distributions, chosen at boot time. Most of them support creating a persistence file or partition to allow you to save files or install additional applications.

Every time I need one, I find that a different one is recommended. Invariably it is not packaged for my current distribution.

This is a set of instructions for creating a multiboot USB stick with optional persistence for one of the installed distributions.

Get ISOs

Download whatever Live disk images you want to install. Good ones are the Ubuntu install images, ArchLinux, Debian etc. Software updates will consume a large amount of space and Kernel upgrades are likely impossible: Choose recently updated images.

Partition USB stick

Use your favourite partitioning tool to create a FAT32 partition at the beginning of the drive. It needs to be large enough to contain all the disk images (ISOs) that you want to boot from, plus some space for the grub boot loader.

Using fdisk:

# fdisk /dev/sdX

Welcome to fdisk (util-linux 2.34).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Command (m for help): o
Created a new DOS disklabel with disk identifier 0xf0e82e51.

Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1):
First sector (2048-15633407, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-15633407, default 15633407): +2G

Created a new partition 1 of type 'Linux' and of size 2 GiB.
Partition #1 contains a vfat signature.

Do you want to remove the signature? [Y]es/[N]o: y

The signature will be removed by a write command.

Command (m for help): t
Selected partition 1
Hex code (type L to list all codes): b
Changed type of partition 'Linux' to 'W95 FAT32'.

Command (m for help): w
The partition table has been altered.
Syncing disks.

The above creates a 2G partition of the FAT32 type. My drive previously contained a FAT32 file system, hence the warning.

Create file system

Create a FAT32 file system in the newly created partition:

# mkfs.fat -F 32 /dev/sdX1

Install Grub

Mount your FAT32 partition and create a grub directory:

# mount /dev/sdX1 /mnt/multiboot
# mkdir /mnt/multiboot/grub

and install grub:

# grub-install /dev/sdX \
        --target=x86_64-efi \
        --efi-directory=/mnt/multiboot/ \
        --boot-directory=/mnt/multiboot/boot \
        --removable

Copy ISOs

Copy all ISOs that you want to be able to boot to some location on the FAT32 partition:

# mkdir /mnt/multiboot/iso_boot
# cp /tmp/all_my_isos/*.iso /mnt/multiboot/iso_boot/

Configure Grub

Create a grub configuration file at /mnt/multiboot/boot/grub/grub.cfg and create one or more entries for every image that you want to boot. A grub configuration for booting just Xubuntu 18.04 without persistence looks like this:

set timeout=10
set default=0
insmod loopback
insmod all_video

menuentry "Run Xubuntu 18.04 64 bit" {
        loopback loop /iso_boot/xubuntu-18.04.2-desktop-amd64.iso
        set gfxpayload=keep
        linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=/iso_boot/xubuntu-18.04.2-desktop-amd64.iso quiet splash ---
        initrd (loop)/casper/initrd
}

The paths /casper/vmlinuz and /casper/initrd correspond to the locations of the kernel and initramfs inside the ISO respectively, which may be different between different distributions.

You can list the content of the ISO as follows:

# modprobe loop
# losetup /dev/loop0 /tmp/os_image.iso
# partprobe /dev/loop0
sh: dmidecode: command not found
Warning: The driver descriptor says the physical block size is 2048 bytes, but Linux says it is 512 bytes.
# mount /dev/loop0 /mnt/tmp
mount: /mnt/tmp: WARNING: device write-protected, mounted read-only.

You can now list the contents of the ISO with standard tools. Look for files which contain vmlinuz and initrd or initramfs in the name.

The kernel options will likely also be different for different distributions. Ideally look at the grub configuration for the ISO that you want to install and copy the relevant kernel options from there.

Unmount the ISO and destroy the loop device when you are done:

# umount /mnt/tmp
# losetup -d /dev/loop0

Enable Persistence (Optional)

On Ubuntu, almost certainly on Ubuntu based distributions, probably on Debian and friends and maybe on other distributions, you can make use of a separate partition on the USB stick for persistence. Setup is surprisingly trivial, but it will only be possible to enable this for a single installed image.

Create another partition on the USB stick and format it with the file system of your choice:

# fdisk /dev/sdX

Welcome to fdisk (util-linux 2.34).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): n
Partition type
   p   primary (1 primary, 0 extended, 3 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (2-4, default 2):
First sector (4196352-15633407, default 4196352):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (4196352-15633407, default 15633407): +4G

Created a new partition 2 of type 'Linux' and of size 4 GiB.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

# mkfs.ext4 -L casper-rw /dev/sdX2
mke2fs 1.45.2 (27-May-2019)
Creating file system with 1048576 4k blocks and 262144 inodes
Filesystem UUID: f86ba47b-9049-4970-9050-07e95d7d0743
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and file system accounting information: done

Make sure the file system has the label casper-rw. The size of the partition needs to be large enough to hold all the changes you make. If you intend to update software on the USB stick you will need a few Gigabytes at least.

Finally edit the grub configuration file on the USB drive to add the option to boot with persistence enabled:

menuentry "Run Xubuntu 18.04 64 bit - Persistent, in RAM" {
        loopback loop /iso_boot/xubuntu-18.04.2-desktop-amd64.iso
        set gfxpayload=keep
        linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=/iso_boot/xubuntu-18.04.2-desktop-amd64.iso quiet splash persistent toram ---
        initrd (loop)/casper/initrd
}

The persistent option enables the persistence. The kernel (or the initramfs) will look for a partition labeled casper-rw on boot to use for the persistence. A bonus: the toram option on Ubuntu will load the contents of the USB stick into RAM on boot, enabling relatively snappy behaviour after the boot process completes.

I'm not sure what the limits are on the choice of file system for the persistence partition. ext4 definitely works but f2fs, which is otherwise a desirable choice, did not work out of the box. I haven't investigate further.

links

social