Most of "household" portable USB drives have a single whole-drive partition formatted as FAT32.
In the past years I remember having seen some recipes of installing bootable live Ubuntu system on such drives which did not require shrinking FAT32 partition, adding more partitions, removing anything existing, etc.
But as I also remember, none of such recipes had become widely recommended and usable.
So I devised my own way of doing this which I document here.
Only 1 folder and 1 image file will be added to your USB drive; all other files you had there will be kept intact.
Also you could remove these folder & file to free up your storage space in any time of the future.
An already-running Ubuntu system on some PC with USB slot will be required to create this portable system.
Most commands below will require root permissions, so open root's shell with sudo -s in advance.
- Plug your USB drive into your PC.
Looking into dmesg, lsblk or blkid printout, notice what name it received.
In simple cases, device name would be 'sdb' and partition name would be 'sdb1'.
Let's assign PART=sdb1 and refer to it as $PART below. - Mount this partition and go into its top-level folder :
mkdir /tmp/$PART
mount /dev/$PART /tmp/$PART
cd /tmp/$PART
df -h .
Ensure that you have at least 5 GB free on this drive, else your root filesystem will be too constrained - mkdir ubuntu-boot
time fallocate -l 4095M ubuntu.loop
ubuntu-boot folder will be mounted as /boot on live system and will hold grub2, vmlinuz and initrd bootable files;
ubuntu.loop will hold the root file system.
4096M/4G is file size limitation on FAT32, most probably you could not allocate more.
give fallocate enough time to complete filling the file with zeros (10..20 minutes on slow flash drive). - losetup -f ubuntu.loop
losetup -a
Notice what device name it received.
In simple cases it would be loop0, so let's assign LOOP=loop0 and refer to it as $LOOP below - apt install f2fs-tools
mkfs.f2fs /dev/$LOOP
mkdir /tmp/$LOOP
mount /dev/$LOOP /tmp/$LOOP
cd /tmp/$LOOP
df -h .
this will create root FS and prepare it for population.
you may choose to create ext4 FS for rotating HDD; f2fs is recommended for solid-state (flash) storage
df should show you about 3.6..3.8 free GBs in your FS - apt install debootstrap
time debootstrap bionic . http://ua.archive.ubuntu.com/ubuntu
echo $?
df -h .
du -sh .
give debootstrap enough time to complete on slow flash drive (10 minutes or more) .
check debootstrap exit code and review debootstrap.log to ensure everything has completed well (300 MB or more could be used after successful debootstrap).
debootstrap should work equally well for other Ubuntu or Debian releases,
my only doubt is with its ability to create 64-bit roots from under 32-bit system - mkdir host
that's needed to access mounted FAT32 partition on booted-up system, see /usr/share/initramfs-tools/scripts/local for details - now create a script file in /tmp/$LOOP/root/runme.sh with the following content :
set -x -e [ $( id -u ) = 0 ] # fail for non-root root=$( dirname $0 | sed 's/root.*//' ) cd $root for i in dev dev/pts proc run sys tmp var/cache/apt/archives do mount --bind /$i ./$i done mount --bind / ./mnt chroot . || : while fgrep $root /proc/mounts do fgrep $root /proc/mounts | while read h i j do umount $i || : done done
- Create a script in /tmp/$LOOP/root/complete.sh with the following content :
set -x -e [ "$COMPLETE" = "y" ] echo /host/ubuntu-boot /boot none bind 0 0 >> /etc/fstab echo tmpfs /tmp tmpfs defaults 0 0 >> /etc/fstab mount /dev/$PART /host mount /boot echo do_symlinks = no >> /etc/kernel-img.conf # else focal won't install linux-image on FAT set -- $(cat /etc/apt/sources.list) { for i in $3 $3-security $3-updates do echo $1 $2 $i main restricted universe multiverse done echo $1 http://archive.canonical.com/ubuntu $3 partner } > /etc/apt/sources.list.d/$3.list sed -i '/^ *deb/s/^/#/' /etc/apt/sources.list apt update apt install linux-image-generic # this will pull in grub-pc ird=$(find /boot/ -type f -iname 'initrd.img*' | xargs ls -t | head -n1) t=$(mktemp) lsinitramfs $ird > $t for i in f2fs libcrc32c crc32_generic crc32-pclmul do fgrep '/'$i'.ko' $t || echo $i >> /etc/initramfs-tools/modules done # fix for f2fs support in bionic rm $t apt install f2fs-tools dosfstools # this will update initrd eval $(blkid | grep /$PART | egrep -o ' UUID=[^ ]+') # echo $UUID echo GRUB_CMDLINE_LINUX_DEFAULT=\"root=UUID=$UUID loop=ubuntu.loop rw\" >> /etc/default/grub grub-install /dev/$(echo $PART | tr -d '0-9') # install into MBR mount /dev/$PART /tmp/$PART # for grub-probe to succeed in focal update-grub fgrep vmlinuz /boot/grub/grub.cfg || { echo GRUB_DEVICE=/dev/$PART >> /etc/default/grub # help bionic discover boot files update-grub } for i in en wl # initialize simplest config for systemd-networkd do cat << EOF1 > /etc/systemd/network/$i.network [Match] Name=$i* [Network] DHCP=ipv4 EOF1 done systemctl enable systemd-networkd - Use runme.sh to enter into your newly-created root :
export PART LOOP
sh /tmp/$LOOP/root/runme.sh - Use complete.sh to do some setup within there :
COMPLETE=y sh /root/complete.sh
adduser user # choose your favorite name for login, and give it some password
adduser user sudo # don't forget to make it a sudoer
exit # from your chroot session - Wait for umounts to complete. Then do
cd ..
umount /tmp/$LOOP
losetup -d /dev/$LOOP
umount /tmp/$PART
exit # from your sudo -s root session
now you are free to remove your USB drive from your PC and test its bootability on some real or virtual hardware.
Finally it should be noted that this scenario will work only for MBR partition scheme and legacy BIOS or UEFI+CSM boot.
For pure UEFI boot, different actions should be needed, but I did not have enough time and hardware to test it.
Some workarounds for grub-pc and initramfs-tools are related to Ubuntu 18.04 Bionic and might not be needed in Ubuntu 20.04 Focal or later (added some conditions to apply these fixes only when necessary).
The most unpleasant thing in bionic is that update-grub refuses to enumerate your vmlinuz/initrd files in /boot until you add GRUB_DEVICE= into /etc/default/grub , and when booting on different hardware, this device name may be different. So watch closely and if you see that update-grub ignores your /boot contents, you probably need to adjust GRUB_DEVICE= in /etc/default/grub and restart update-grub . I looked into the logic of bionic's /etc/grub.d/10_linux and I was unable to invent a better way to make it work as I needed.
I tested these scripts on 32-bit 18.04/Bionic and 64-bit 20.04/Focal. On Focal I successfully created both 32-bit bootable Bionic and 64-bit bootable Focal.
Did not replay this scenario on Debian but I feel it is going to be mostly similar
After completion of this scenario you could log in locally from text VGA console and have at least your wired Ethernet interface configured by DHCP.
GUI configuration will be reviewed in some later article.