I use Puppy Linux on my laptop. Everyone I mention this to is like “Oh yeah Puppy, I used that on a flash drive that one time at school” or something to that extent. That’s kinda where I was when I tried it too; I didn’t want to buy a boot drive for my thinkpad when I had a slim USB stick I could just plug in with 128 gigs of storage, and I had heard puppy was fast for that. I wasn’t wrong either, this thing runs way better than it has any right to given the speed of this storage device. But what’s even cooler is how easy it is to remix a puppy linux distribution into your own custom puppy that you can share with others.
But first, a moment from our- oh wait no they aren’t sponsoring me.
Before I get into the weird complex stuff Puppy can do, I want to direct your attention somewhere that the mixtape culture is already alive and well in the linux world: Raspberry Pi. Given raspi’s pervasiveness, and their dedication to keeping Raspberry OS backwards compatible with basically all of their hardware, its really common for raspi software to get distributed as a
.img disk image. Don’t worry about installation, configuration, all that stuff, unless you really want to. If you want a plug and play experience with kodi, plex, emulators, ad-blocking, or anything else really, you can usually just download a .img file and put it on your SD card.
This only works because it’s all targeting the same hardware, with the same boot process. Surely we couldn’t do anything like that in x86 land, across a wide variety of hardware. Doing something like that would be nearly imposs-
Puppy Linux is Hecking Cool
So puppy linux actually is sort of a meta-distribution. There’s ubuntu-based puppies, debian puppies, slackware puppies. What’s shared across all of them is the way it uses union filesystems. Bear with me because I’m going to get a bit in the weeds for a sec.
A union filesystem (in this case, aufs), merges a bunch of filesystems as layers into a single filesystem. If two filesystems have a file at the same filepath, the top filesystem will provide the file and hide the file in the bottom filesystem. Think of it like stacking pieces of paper:
You can also decide what happens when a new file gets created, which layer it gets written to.
Puppy has a layer stack that looks something like this:
- in-ram write cache, starts empty at boot
- pupsave, with user-created data and user-installed packages
- puppy (read only) - core puppy system
- adrv (read only) - pre-installed application package set
- fdrv (read only) - firmware
- zdrv (read only) - kernel modules and such
The read-only layers here are squashfs files. If you’ve never heard of those, imagine a zip file that’s optimized to be mounted as a read-only disk image, and be really fast
Writes get written to the in-memory write cache. You can flush the write cache to the pupsave, either manually, periodically, or at shutdown. This is pretty great for keeping my system feeling snappy despite the awful usb stick performance, by deferring the writes until I’m not busy doing anything, but ultimately it’s not my focus here.
The pupsave is just a folder on my usb drive with any changes I’ve made to the system in it. There’s nothing special about it really. That’s what’s so cool though.
Let’s say you messed up, really bad. Oops, your system won’t boot anymore. In the boot menu, you can boot without the pupsave loaded and now you’re back to the same state you had with a fresh install. That means your installation is your rescue filesystem! Then you can go into the pupsave folder, delete or fix whatever file broke things, and reboot back into your working system.
Puppy Linux Mixtapes
This layering setup makes it really easy to make your own remix of the core setup. Don’t like the defaults? There’s a solution!
- extract the puppy.sfs or adrv.sfs with
unsquashfs(depending on what you want to change)
- delete or add whatever you want
- recompress it with
- rename the old one and put the new one in its place.
From here you can use your new changes, send them to other people, whatever you want to do. There’s even tools to turn this into a new .iso file that you can install from.
We can turn pupsaves into mixtapes too!
Since the pupsave is just a folder with files, if you want to share your system with someone, all you have to do is turn your pupsave folder into a .sfs file with
mksquashfs and send it to them. Puppy lets you load arbitrary .sfs files, so they can just boot up without their pupsave, load your .sfs file on top, and they are now effectively using your system. Then they can make changes on that, squash those up, and send them back to you or other people too.
You could even squash up just part of your system, like an application that was particularly tricky to install, or a collection of software for making music, or something that replaces all the coreutils with rust rewrites. Literally whatever you want!
What would make this even cooler?
This kind of thing gives me all kinds of ideas for ways to expand on it.
Consider, for example a system built like this:
- system user data
- system packages
During normal system use packages can get installed to the package layer, and changes made to user data. Overwrite your package configs carefree! If you ever break it, just delete the config file and reboot or something, because you’re not destroying the package defaults by changing files on your system, just shadowing them.
Now let’s add two more layers:
- system user data
- system packages
- common user data
- common packages
Imagine you’ve got three computers all running the same OS. One is arm, one is x86, one is PowerPC. There’s certain packages you want on all of the systems, and certain configurations and files you want everywhere. Those could go in the
common layers, and get automatically synced between them all.
fzf on every system you use? Done, with the appropriate versions installed for your CPU. Core dotfiles synced without worrying about accidentally adding system-specific dotfiles to the sync? Yup! Tired of editing
/etc/hosts everywhere? Well, maybe it’s time to just set up your own DNS server, but still, it’s an option. And of course you could bundle these up and share them with anyone else you want to.
Then add chroots.
Say you’ve got an x86 program you want to use on arm. No wine nonsense, you just only have an x86 executable because that’s what the devs built for. You could chroot into a union fs where the
system packages layer was replaced with an exact duplicate of itself, except with all x86 packages instead of arm. Add
qemu-user into the mix, and now inside that chroot your system is exactly the same as outside except you can run x86 programs.
If you’re into containers you could start using those here too, but you containerfolk are likely to be used to thinking with layers anyway.
Bringing it Back to Embedded
Imagine a world where custom raspberry pi images are distributed as .sfs files you drag and drop into your SD card. Edit a config txt to tell it which one to load at boot, it would layer that sfs overtop the core Raspberry OS. Each .sfs could automatically get its own save folder so they aren’t stomping on each others’ data. No more downloading the same base system 5 times over along with the bloated .img file. Less wearing out SD cards from reflashing them as needs change. Maybe we could even build this on top of something that isn’t overly platform specific Raspberry OS.
Imagine that world.
I don’t have a nice conclusion to tie this all together honestly. There’s a lot of cool ideas I’d like to explore to keep making mixtape culture easier on Linux. I feel it’s really important as a more user-focused aspect of open source, in contrast to all the focus on what open source means for developers. I know I’ve mentioned squashfs a TON here but it’s really not necessary for it, as Raspberry Pi proves. I just think the technology is neat and wanted to share it with you too, alongside the other strawberry swirl of ideas in this post.
One way or another, I hope you enjoyed reading. Bye!