Here’s a guide to installing a bootable CD-ROM ISO image as a bootable partition on a (USB) drive, while using other partitions as normal filesystems. The most complicated part is partitioning and formatting the drive, which is well documented elsewhere, but I didn’t see anyone else explaining exactly how to do this trick.

Use Cases and Alternatives

If you just want an OS on a drive you can carry around, try just installing the OS normally on a portable drive. Most FOSS OS installers are flexible enough to allow that. System upgrades will work as normal.

If you just want to use an existing live CD ISO, but with the ability to save data, you can extract the files from the ISO onto a regular drive, and boot the drive with an appropriate bootloader. unetbootin is a classic tool for doing this automatically, and you can also do it manually. This works with common *nix live CDs, but isn’t guaranteed to work with arbitrary bootable CDs. You also may or may not be able to upgrade this system easily.

If you just want to customise a CD a bit, in principle you can extract the files, tweak them and then rebuild an ISO. This should work with practically any CD, and there’s a detailed example in the SystemRescueCd docs. I’ve tried this before, but in practice it gets tiring to rebuild an ISO every time a file needs to be changed. Also, merging changes with an upgraded version of the CD might be difficult.

I use a multi-partition bootable USB drive as a system recovery drive. It has a recent install CD image written to partition #1, and a bunch of my own config files and scripts, etc, on a normal ext filesystem on partition #2. The ISO can be updated by downloading a new one and writing it using the tools below, and I can update my files by mounting the second partition as a normal drive. I actually had to use this drive last week for the second time when my computer’s motherboard died and I had to reinstall my stuff on a new machine. It was a useful tool, and that’s what motivated me to write this guide.

Another possibility is installing multiple live CDs to one drive – say, for a demo. I’ve never needed to do this, but you’d want to install a chaining bootloader on one partition to provide a nice menu for booting the other partitions. That’s not covered by this guide.

Warning

Writing a live CD ISO to a disk will practically destroy all data that was on it. Of course, that’s fine if you’re writing to a blank drive you just bought from a shop, but if you mess up and write to your main hard drive instead, then complaining to a blogger in Australia won’t help you get your data back. I can’t take responsibility for anything you might break, so if you’re not comfortable, work on a new virtual machine or something else you’re willing to lose.

If you’ve never done any partitioning or formatting of disks before, I recommend reading one of the many guides out there.

The Guide

The specific commands I give are for a *nix system, but if you know what you’re doing, I guess you could make it work on Windows (maybe with help from Cygwin).

Many of these commands will require root or administrator rights.

1. Prepare the ISO

This part doesn’t need root privileges. isohybrid is a tool from the Syslinux project that makes ISOs bootable from regular (non-optical) disks. It can also make ISOs bootable from a partitiion if you use the --partok command line flag. Your OS’s package manager probably has it under syslinux or syslinux-utils.

Here’s an example using a copy of SystemRescueCd:

$ isohybrid --partok systemrescuecd-x86-4.7.2.iso

The file is modified in place.

If you came here just wanting to know how to make a bootable USB drive, and don’t care about creating extra partitions, then you can make things much simpler. Just run isohybrid on a bootable ISO without the --partok, and write it directly to the drive just like in the MBR installation step below.

2. Partition the drive

If the warning above didn’t make it clear: make sure you know which drive you want to write to. Do not just copy my examples. On a *nix system, the drive will probably have a name like /dev/sdb or something, but check your OS documentation. You want the disk, not any of its partitions (e.g., /dev/sdc not /dev/sdc2). On a modern *nix system, the device name will disappear from /dev if you remove the drive, and reappear if you plug it in again.

Update: On modern modern *nix systems, /dev/disk/by-id/ contains meaningful disk names that link to the low-level names, making it easier to recognise the right one. The same caveat of making sure you’re not writing to the wrong disk still applies.

Another way to check is to try reading data from the drive (as root):

# dd if=/dev/sdf of=/dev/null

Many USB drives have an LED that’ll flash like mad when you do this, and stop when you kill the read with Ctrl+C. It’s a good idea to run mount without arguments and/or read /etc/fstab to familiarise yourself with what drives are used as what. If anything from the drive you’re planning to use is mounted, it’s saner to unmount it before messing with the partition table.

There are many tools for partitioning drives; some are friendlier than others. This guide uses old-fashioned MBR partitioning, not any new-fangled GPT partitioning. It’s well-supported and good enough for a small USB drive. We want one bootable partition that’s big enough for the ISO, and any other partitions as needed. Here’s an example session using fdisk on a new 8GB drive, creating a 1GB partition for the live CD image, and reserving the rest for an ext4 filesystem:

 # fdisk /dev/sdz

 Welcome to fdisk (util-linux 2.26.2).
 Changes will remain in memory only, until you decide to write them.
 Be careful before using the write command.
 
 
 Command (m for help): m
 
 Help:
 
   DOS (MBR)
    a   toggle a bootable flag
    b   edit nested BSD disklabel
    c   toggle the dos compatibility flag
 
   Generic
    d   delete a partition
    l   list known partition types
    n   add a new partition
    p   print the partition table
    t   change a partition type
    v   verify the partition table
 
   Misc
    m   print this menu
    u   change display/entry units
    x   extra functionality (experts only)
 
   Script
    I   load disk layout from sfdisk script file
    O   dump disk layout to sfdisk script file
 
   Save & Exit
    w   write table to disk and exit
    q   quit without saving changes
 
   Create a new label
    g   create a new empty GPT partition table
    G   create a new empty SGI (IRIX) partition table
    o   create a new empty DOS partition table
    s   create a new empty Sun partition table
 
 
 Command (m for help): p
 Disk /dev/sdz: 7.2 GiB, 7746879488 bytes, 15130624 sectors
 Units: sectors of 1 * 512 = 512 bytes
 Sector size (logical/physical): 512 bytes / 512 bytes
 I/O size (minimum/optimal): 512 bytes / 512 bytes
 Disklabel type: dos
 Disk identifier: 0x12b90d40
 
 Device     Boot Start      End  Sectors  Size Id Type
 /dev/sdz1        8064 15130623 15122560  7.2G  c W95 FAT32 (LBA)
 
 Command (m for help): o
 Created a new DOS disklabel with disk identifier 0x9d4eb45a.
 
 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): 1
 First sector (2048-15130623, default 2048): 
 Last sector, +sectors or +size{K,M,G,T,P} (2048-15130623, default 15130623): +1G
 
 Created a new partition 1 of type 'Linux' and of size 1 GiB.
 
 Command (m for help): a
 Selected partition 1
 The bootable flag on partition 1 is enabled now.

 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): 2
 First sector (2099200-15130623, default 2099200): 
 Last sector, +sectors or +size{K,M,G,T,P} (2099200-15130623, default 15130623): 
 
 Created a new partition 2 of type 'Linux' and of size 6.2 GiB.
 
 Command (m for help): p
 Disk /dev/sdz: 7.2 GiB, 7746879488 bytes, 15130624 sectors
 Units: sectors of 1 * 512 = 512 bytes
 Sector size (logical/physical): 512 bytes / 512 bytes
 I/O size (minimum/optimal): 512 bytes / 512 bytes
 Disklabel type: dos
 Disk identifier: 0x9d4eb45a
 
 Device     Boot   Start      End  Sectors  Size Id Type
 /dev/sdz1  *       2048  2099199  2097152    1G 83 Linux
 /dev/sdz2       2099200 15130623 13031424  6.2G 83 Linux
 
 Command (m for help): w
 The partition table has been altered.
 Calling ioctl() to re-read partition table.
 Syncing disks.

3. Format the writable partitions

You can use any filesystem you fancy here (although some systems might complain if the partition type doesn’t match). A basic FAT32 filesystem has broad support across operating systems and devices, but doesn’t have *nix features like owners and permissions and links. ext4 is the most popular filesystem on GNU/Linux systems today, and it’s suitable for my rescue USB drive. If you want, you can tinker with the various options in man mkfs.ext4 and do things like, say, disabling journalling, but realistically the out-of-the-box settings work fine for this job. My first partition is the boot partition, so partition #2 is for files:

# mkfs -t ext4 /dev/sdz2

4. Write the ISO

After the ISO has been converted with isohybrid, it just needs to be written bit-for-bit to the boot partition (/dev/sdz1 in my example). dd is the command for that:

# dd if=systemrescuecd-x86-4.7.2.iso of=/dev/sdz1 bs=1M

The bs (“blocksize”) parameter is not critical.

5. Write a Master Boot Record (MBR)

The MBR is what makes the drive bootable. When you boot from the drive, it searches for a bootable partition and hands over the boot process to it. The Syslinux project provides MBRs, which are tiny files (less than 512 bytes) of mostly machine code. Debian also has a convenient package called mbr that provides a simple command install-mbr. The MBR needs to be installed to the start of the disk, not to a partition. Here are some examples of installing to /dev/sdz (only one command is necessary):

# install-mbr /dev/sdz
# dd if=/usr/share/syslinux/mbr.bin of=/dev/sdz

6. Finish!

If all went well, the drive should now be bootable. You can mount the second partition of the drive as normal (e.g., mount /dev/sdz2 /mnt).

If you want to upgrade the CD image, just download the new ISO, convert it with isohybrid, and write it to the boot partition. There’s no need to reinstall the MBR, or redo any of the other steps.