Creating a Debian bootable live USB

We can install a full Linux distribution in a USB drive, and use it to boot a system. That is called a "live USB," and it can be used for recovery, as a portable environment, etc.

In this article we explain how to install a Debian GNU/Linux OS in a USB drive as if it was a hard disk. We will use Debian's own debootstrap to populate the root partition and syslinux as a bootloader (it is simpler than the standard grub).

Obs: to ease copy-and-pasting, we show the commands without prompt, and prepend > to the output of commands on most examples.

1 Partitioning

After inserting the USB drive, it will appear as a block device under /dev, usually sd[a-z]. Take a note on the device name. We will use /dev/sdc through the examples.

Create two partitions on our USB drive: one with 256MB for the /boot that will hold the syslinux bootloader and the Linux kernel; and another with all the rest of the space, that will hold the root filesystem.

There are many utilities you can use to partition the USB drive: parted, fdisk, etc.

For example, using fdisk, run it on /dev/sdc:

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

You are left at fdisk's command prompt. Create the boot partition:

  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-31350782, default 2048):
  > Last sector, +sectors or +size{K,M,G,T,P} (2048-31350782, default 31350782): +256M
  > Created a new partition 1 of type 'Linux' and of size 256 MiB.

Set its type to FAT16:

  Command (m for help): t
  > Selected partition 1
  > Hex code (type L to list all codes): 6
  > If you have created or modified any DOS 6.x partitions, please see the fdisk documentation for additional information.
  > Changed type of partition 'Linux' to 'FAT16'.

Mark it as active:

  Command (m for help): a
  > The bootable flag on partition 1 is enabled now.

Create the root partition:

  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 (526336-31350782, default 526336):
  > Last sector, +sectors or +size{K,M,G,T,P} (526336-31350782, default 31350782):
  > Created a new partition 2 of type 'Linux' and of size 14.7 GiB.

Check that they were created:

  Command (m for help): p
  > Disk /dev/sdc: 15 GiB, 16051600896 bytes, 31350783 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: 0x13090bb3
  > Device     Boot  Start      End  Sectors  Size Id Type
  > /dev/sdc1  *      2048   526335   524288  256M  6 FAT16
  > /dev/sdc2       526336 31350782 30824447 14.7G 83 Linux

Save and exit:

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

We now have a /dev/sdc1 that will be our /boot, and /dev/sdc2 that will be our root file system. Observe that the boot partition has a MS-DOS type - that is required syslinux.

(the instructions above are heavily based on using-syslinux-to-boot-debootstraped)

2 Installing the bootloader

Create a FAT16 filesystem on the boot device:

  mkdosfs -n LINUXBOOT /dev/sdc1
  > mkfs.fat 3.0.27 (2014-11-12)

Install syslinux on it:

  syslinux /dev/sdc1

3 Installing the base system in the root partition

Create the filesystem:

  mkfs.ext4 /dev/sdc2
  > mke2fs 1.42.12 (29-Aug-2014)
  > Creating filesystem with 3853055 4k blocks and 964768 inodes
  > Filesystem UUID: 68d66fd5-97f2-46ed-aee6-dad6f228a172
  > Superblock backups stored on blocks:
  >         32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208
  > Allocating group tables: done
  > Writing inode tables: done
  > Creating journal (32768 blocks): done
  > Writing superblocks and filesystem accounting information: done

You can use any filesystem here, as long as it is supported by your future kernel.

Mount the partition and use debootstrap to install the base files on it:

  mkdir -p usbroot
  mount -t auto /dev/sdc2 usbroot
  debootstrap stable usbroot
  > I: Retrieving Release.gpg
  > I: Checking Release signature
  > I: Valid Release signature (key id 75DDC3C4A499F1A18CB5F3C8CBF8D6FD518E17E1)
  > I: Retrieving Packages
  > I: Validating Packages
  > I: Resolving dependencies of required packages...
  > I: Resolving dependencies of base packages...
  > I: Found additional required dependencies: acl adduser dmsetup insserv libaudit1 libaudit-common libbz2-1.0 libcap2 libcap2-bin libcryptsetup4 libdb5.3 libdebconfclient0 libdevmapper1.02.1 libgcrypt20 libgpg-error0 libkmod2 libncursesw5 libprocps3 libsemanage1 libsemanage-common libslang2 libsystemd0 libudev1 libustr-1.0-1 procps systemd systemd-sysv udev
  > I: Found additional base dependencies: libdns-export100 libffi6 libgmp10 libgnutls-deb0-28 libgnutls-openssl27 libhogweed2 libicu52 libidn11 libirs-export91 libisccfg-export90 libisc-export95 libmnl0 libnetfilter-acct1 libnettle4 libnfnetlink0 libp11-kit0 libpsl0 libtasn1-6
  > I: Checking component main on
  > I: Retrieving acl 2.2.52-2
  > (...)
  > I: Configuring libc-bin...
  > I: Configuring systemd...
  > I: Base system installed successfully.

4 On-root configuration

We will have to chroot into our root filesystem to configure it further.

Mount the boot device and the default ones inside the root mount point:

  mount -t devtmpfs dev       usbroot/dev
  mount -t devpts   devpts    usbroot/dev/pts
  mount -t proc     proc      usbroot/proc
  mount -t sysfs    sysfs     usbroot/sys
  mount -t auto     /dev/sdc1 usbroot/boot

chroot into root:

  chroot usbroot /bin/bash

Set the root user password:

  > Enter new UNIX password:
  > Retype new UNIX password:
  > passwd: password updated successfully

Install the Linux kernel:

  apt-get install --no-install-recommends -y linux-image-amd64 syslinux
  > Reading package lists... Done
  > Building dependency tree... Done
  > The following extra packages will be installed:
  >   initramfs-tools klibc-utils libklibc libuuid-perl linux-base linux-image-3.16.0-4-amd64
  > Suggested packages:
  >   bash-completion linux-doc-3.16 debian-kernel-handbook grub-pc grub-efi extlinux
  > Recommended packages:
  >   busybox busybox-initramfs busybox-static firmware-linux-free irqbalance
  > The following NEW packages will be installed:
  >   initramfs-tools klibc-utils libklibc libuuid-perl linux-base linux-image-3.16.0-4-amd64 linux-image-amd64
  > 0 upgraded, 7 newly installed, 0 to remove and 0 not upgraded.
  > Need to get 34.1 MB of archives.
  > After this operation, 164 MB of additional disk space will be used.
  > (...)
  > Setting up linux-image-amd64 (3.16+63) ...
  > Processing triggers for initramfs-tools (0.120) ...
  > ln: failed to create hard link '/boot/initrd.img-3.16.0-4-amd64.dpkg-bak' => '/boot/initrd.img-3.16.0-4-amd64': Operation not permitted
  > update-initramfs: Generating /boot/initrd.img-3.16.0-4-amd64

We now have to set up our mount points in the /etc/fstab of the USB drive, but if we simply use /dev/sdc* as the devices, we will have trouble mounting it on other systems with a different number of hard drives. To have stable mount points, we use the UUID - universal unique identifiers - of the filesystems. Use blkid to find out the values of your identifiers:

  > (...)
  > /dev/sdc1: SEC_TYPE="msdos" UUID="2420-26B1" TYPE="vfat" PARTUUID="13090bb3-01"
  > /dev/sdc2: UUID="68d66fd5-97f2-46ed-aee6-dad6f228a172" TYPE="ext4" PARTUUID="13090bb3-02"

In this example, the UUID of the boot filesystem is 2420-26B1, and the UUID of the root filesystem is 68d66fd5-97f2-46ed-aee6-dad6f228a172. Use them to populate /etc/fstab:

  echo 'UUID=68d66fd5-97f2-46ed-aee6-dad6f228a172 /     ext4 defaults,noatime 0 0' >  etc/fstab
  echo 'UUID=2420-26B1                            /boot vfat defaults         0 0' >> etc/fstab

Figure out the name of the kernel and initrd installed on the boot partition:

  ls boot/vmlinuz* boot/initrd*
  > boot/initrd.img-3.16.0-4-amd64  boot/vmlinuz-3.16.0-4-amd64

And use them with the UUIDs to create the boot/syslinux.cfg file, with the following contents:

  default linux
  timeout 1
  prompt 1

  label linux
      kernel vmlinuz-3.16.0-4-amd64
      append initrd=initrd.img-3.16.0-4-amd64 root=UUID=68d66fd5-97f2-46ed-aee6-dad6f228a172 ro

Finally, write syslinux's master boot record on the USB drive:

  cat /usr/lib/SYSLINUX/mbr.bin > /dev/sdc

5 Closing up

We are now ready to leave the chroot and umount all devices:

  umount usbroot/dev/pts
  umount usbroot/dev
  umount usbroot/proc
  umount usbroot/sys
  umount usbroot/boot
  umount usbroot

You can now remove the USB drive and use it to boot any computer.


This article is, in fact, basically a rehash of the following references with the UUID part added.