I recently wanted to make a slightly modified persistent bootable USB stick running a recent version Ubuntu. I made some notes and have put them here in case they’re useful to anyone else. It’s a bit of a manual process which could probably be streamlined / automated. This was just what I did as a one-off, take from it what you will.
USB3 sticks in a USB3 port work best as USB2 can be a bit on the slow side, especially for IO intensive operations like package installation or compiling.
Note: A few people have pointed out the fragility and short lifespan of USB sticks. This same procedure can be used to install on a hard disk or SSD in a USB enclosure. Once the image is copied to the external storage, simply use gparted to resize it up to take all available space.
The goal I had was to make an image which can be copied to USB stick to provide a persistent bootable Ubuntu SDK development environment. This could be useful for people who don’t run Ubuntu as their primary OS (Yes, these people exist, I know right!?) but want to dabble in Ubuntu application development. It’s also handy if you’re running an App Dev School where the computers aren’t yours, or run some other OS. The students could potentially take the sticks away with the full OS and all their work on. Just make the image and then copy it to multiple sticks before the class starts.
I also wanted to make it ask for locale and user details on first boot, so it could be easily configured and used in any language. This is pretty easy given the Ubuntu installer has all of that built in.
I used Ubuntu 15.04 i386 (but also tried with Ubuntu 14.04.1 LTS) and an 8GB USB stick which leaves a couple of GB over for work. Obviously a larger stick gives more space to the user. It turned out though that using an 8GB USB stick was a bit tight for SDK work. I ended up with 76MB left after creating one 15.04 armhf kit. Maybe 8GB is good for desktop and qml/html5 only development (although still a bit tight), but not for cross architecture or other binary builds. 16GB would have enough room for multiple kits and could build binaries for devices.
Some of these steps can be done in the background while you do other things. It’s not a massively time consuming task if you have a decent connection and fast USB stick / hard disk, but as I mentioned, is a bit manual.
The result is a USB stick which you can boot from and work off with data saved to the stick. You can optionally enable home directory encryption during the final end-user setup if that’s important to you.
Step 1 – Prep
Have an 8GB (or larger) USB 3 stick handy. I am using Kingston 8 GB USB 3.0 DataTraveler G4 Flash Drive and later Kingston Technology 16GB Data Traveler G4 USB 3.0 Flash Drive. Faster sticks are available of course, but I wanted something cheap to prototype on.
Have a laptop with a USB 3 port (or ports) and supports kvm. I did all this on my Ubuntu Vivid Vervet (15.04) Thinkpad X220 laptop which has a single USB3 port.
Make a directory on a local disk to store scratch image – will need 16GB or more space
gddrescue on host
Download ubuntu-15.04-desktop-i386.iso from http://releases.ubuntu.com/15.04/. (torrent link).
Step 2 – Installation of base system
Make a blank image on local disk
dd if=/dev/zero of=./disk_image bs=1M count=7500
This should result in a file a bit under 8GB.
alan@deep-thought:/data/usb⟫ dd if=/dev/zero of=./disk_image bs=1048576 count=7500
7500+0 records in
7500+0 records out
7864320000 bytes (7.9 GB) copied, 34.468 s, 228 MB/s
Install Ubuntu into the image using kvm
sudo kvm -m 2048 -cdrom ~/Downloads/ubuntu-15.04-desktop-i386.iso -hda ./disk_image -boot d
This should boot off the ISO
At the A11Y (person = keyboard) icon, hit space
At the boot menu, choose language (this is just language for the installer, user will later choose which language to use)
Press F3 and choose keyboard layout
Press F4 and choose OEM install
Pick “Install” from the menu.
Follow the installer prompts as normal. I configured with no swap, but use the entire disk for an ext4 volume for the root filesystem.
Set a password for the oem user, which will be thrown away later, and the user will get to set their own password.
Shut-down at the end
Step 3 – Install the SDK
This is the part where you make the modifications to the image (if any). I wanted to install the Ubuntu SDK.
Optionally at this point, make a backup of your cleanly installed Ubuntu 15.04.1 system
cp ./disk_image ./ubuntu_15.04_install_backup
Boot the previously created install (note the additional options – these are handy)
sudo kvm -m 2048 -hda ./disk_image -chardev stdio,id=mon -mon mon
Once booted to the desktop, in the terminal on the host at the (qemu) prompt type this to switch the VM to the console (which is faster to do stuff than the GUI ):-
(qemu) sendkey ctrl-alt-f1
Login to the tty with the oem user/password set in Step 2.
Follow the usual guide to install the SDK and update the system:-
sudo add-apt-repository ppa:ubuntu-sdk-team/ppa
sudo apt-get update
sudo apt-get install ubuntu-sdk
sudo apt-get dist-upgrade
sudo apt-get clean
sudo apt-get autoremove
Shut down the vm
sudo shutdown -h now
Optionally at this point, make a backup of your “SDK-installed” (or modified in whatever way you choose) OEM mode Ubuntu 15.04 system
cp ./disk_image ./ubuntu_15.04_install_sdk_oem_backup
Note: At this point you can boot the disk image and do further customisation – maybe adding other packages which may be of use, but I stopped here.
Step 4 – Prepare the OEM image for ‘shipping’
This is the point where we flip the switch in the installed image before handing it off to another user. On first boot they will get prompted to set locale and configure a new user.
Boot the previously created install which has the SDK installed
sudo kvm -m 2048 -hda ./disk_image
Click the “Prepare for shipping to end user” icon on the desktop – this sets the system to be ready for the first-boot experience for a new user
Shut down the system
Step 5 – Test this all worked
Make a copy of the master image for testing
cp ./disk_image ./testing_oem_install
Boot the test image to try it out
sudo kvm -m 2048 -hda ./testing_oem_install
At this point you should be prompted for the usual post-install setup tasks including language / locale / username & password. Setup as you would a normal machine
Open the SDK (or whatever you installed), test it all works
I tried creating a kit and do other SDK related things
Shutdown when done
Delete the test image
Step 6 – Copy the OEM image to a USB stick for shipping / use
Now we have a ‘final’ image (and optionally some backups) we can copy this to a stick for use by us / someone else. We can of course make more than one by doing this step multiple times with different sticks. On my system as you can see it took ~30 mins to copy the image to the stick. Faster, more expensive sticks may be better, these were pretty cheap.
Copy the disk image to an appropriately sized USB stick
sudo ddrescue -d -D --force ./disk_image /dev/sdX
alan@deep-thought:/data/usb⟫ time sudo ddrescue -D -d --force disk_image /dev/sdc
GNU ddrescue 1.19
Press Ctrl-C to interrupt
rescued: 7864 MB, errsize: 0 B, current rate: 1966 kB/s
ipos: 7864 MB, errors: 0, average rate: 4884 kB/s
opos: 7864 MB, run time: 26.83 m, successful read: 0 s ago
Step 7 – Test & use the stick
Put the USB stick in a computer set to boot from external media.
Test that you get a desktop and the usual OEM prompts you got in Step 5.
If that works then you can do step 5 again for the same stick or as many sticks as you have.
Comments and suggestions welcome!