Adventures of installing third-party software on Steam Deck

Ever since I got my Steam Deck I’ve been wanting to use it for other general Linux stuff. Today I use it to supplement my main computer, an Apple Silicon Mac. Having an x86-64 Linux device that I can ssh in to do things like build and use x86-64 Docker images has been useful.1

Thing is, there isn’t really a good way to install stuff that isn’t on Steam or available as a Flatpak. So I tried a few options, and these are my experiences with them.

Disable read-only on the root filesystem

This is definitely the easiest method of them all. Just using steamos-readonly disable will let you install and upgrade packages using pacman like standard Arch Linux. (Make sure to then do steamos-readonly enable after!)

The main problem with this one is that OS upgrades will wipe out changes that you make. This can be countered with a script that auto-reinstalls the packages you want. But there are other potential problems. The OS partition does not have a lot of free space to it; as of SteamOS 3.5, around 2 GiB. If you want to install a lot of packages or some very large ones, that’s gonna give you a bad time.

Additionally, pacman may not be able to properly handle packages that just vanish without a true uninstall. Packages leave files around in places like /etc, and when trying to re-install, it will complain about files that already exist. (One quick way to work around this is --overwrite \* if you don’t care about what it might overwrite.)

One other thing if you specifically try to set up a development environment on Steam Deck. SteamOS strips out a lot of unnecessary files from packages such as man pages and header files. Re-installing may help with this, but it will very quickly wipe out what little free space there is on the OS partition.

Final problem is that the SteamOS package repository is not kept in constant sync with the Arch Linux repository. Packages will be outdated. This affects some worse than others, like web browsers. You could potentially work around this by installing newer packages from archlinux.org, but then you can run into even more issues, especially with SteamOS’s glibc being older.

systemd-sysext

I first heard of systemd-sysext by this blog post by Alberto Garcia. It is basically “system extensions” that are used as an overlay on /usr and /opt. It doesn’t require disabling the read-only seal on the OS partition.

This is a neat idea, but it has more problems to it. You need to manually extract package contents and then pack them into a filesystem image. Because of this, it won’t be properly installed and pacman won’t do things like run install hooks. This one is a bigger problem if what you want to install contains stuff like kernel modules or systemd units. dkms, if it’s even installed, won’t find it and build the modules for you, you have to do it yourself. And systemd won’t automatically load units in an extension because extension images are not loaded until later.

It also only modifies /usr and /opt. Packages that add to /etc and other places need their files copied manually. It’s also still tied to the OS version and extensions need to be rebuilt after every OS upgrade. It does however not have the free space problem of the OS partition, so you can install large packages this way.

I used this method for a while as I was willing to put up with the limitations and install some large packages like VMware Workstation. I automated rebuilding extensions and even the building of kernel modules and setting up systemd units.

I had a script that would use pacman to download packages that were not already installed in the base system, and then extract them to make an extension image. This was when pacman’s database was still at /var/lib/pacman, so using it to do anything but install packages to the system worked fine. However SteamOS 3.5 (still in preview at the time of this writing) moved it to /usr/lib/holo/pacmandb, so now I need to disable read-only on the OS partition to do this.

After my script broke I decided to stop using this method. It required too much manual work to use.

Overlay filesystems

I experimented with Linux’s overlay filesystem. This would “mount” /usr as read-write, but any changes go to another location. (Before SteamOS 3.5, it would be a good idea to make /var/lib/pacman into an overlay too.)

I didn’t try it for very long, mostly because I found unmounting to be problematic. I didn’t realize at the time that this may be because directory overlays on the default SteamOS setup may be problematic due to casefolding on the /home partition (for reference, the root filesystem uses btrfs). This was mentioned in the systemd-sysext blog post.

NOTE: systemd-sysext can also use extensions from plain directories (i.e skipping the mksquashfs part). Unfortunately we cannot use them in our case because overlayfs does not work with the casefold feature that is enabled on the Steam Deck.

I know there also exists rwfus, another solution that makes an overlay on /usr. As I understand it, it uses a btrfs disk image, which is probably how it gets around this issue. I haven’t tried it but it seems neat.

This also runs into the same issue with systemd units is extensions, in that they don’t get loaded automatically.

Nix

I’ve heard about Nix before but never seriously gave it a try. Until I saw that SteamOS 3.5 was including a /nix directory at the root (and offloading it to the /home partition), specifically to make it easier to install without modifying the root filesystem.

This might be the best solution overall. It persists on OS upgrades and it properly installs packages. But it has a learning curve to it. It took me a while to figure out how to use it, and I still feel like I don’t really understand how it manages packages, or how profiles work, and all that.

I’ve also already run into one problem: installing services is a bit more tricky. Stuff like Docker adds systemd units. I can’t get systemd to recognize and use them. I need to manually keep re-linking docker.service and docker.socket to /etc/systemd/system. Only for it to not automatically start up anyway. If anyone has an idea on how to fix this, I would love to know.

My current setup: Nix, for the most part

Considering that it solves the issue of persisting across updates and doesn’t modify the OS partition, it’s the one I currently intend to use going forward.

Given my issue with setting up systemd units, I have decided to combine it with disabling read-only to install Docker with pacman. It’s the easiest way to get it working for now.

Additional note: /usr/local

I don’t know when this change was made but I noticed recently SteamOS includes /usr/local as an “offloaded” directory. That would make installing software there convenient since it will persist in updates.

Turns out I was wrong, it does not offload this directory by default (despite being in /home/.steamos/offload). You could still create your own mount over /usr/local though.

Sidenote to this additional note: I made a Docker image that uses the SteamOS repositories. If you want to build something for SteamOS, consider checking it out.

This whole thing is what I get for trying to use my Steam Deck as anything but a gaming console. 🙃

Other things I didn’t try

I completely forgot the existence of Homebrew on Linux, a port of Homebrew for macOS. uyjulian made a gist about setting it up on Steam Deck. It installs everything to /home/linuxbrew.

Another common solution is Distrobox. As I understand it, it runs other distributions using podman but integrates it with the host OS.

I’m leaving these here in case someone else wants to check them out.

Notes

  1. I know x86-64 images can be used on an ARM64 Mac with emulation, but it can be very slow and sometimes compilers randomly fail. I don’t know why that happens. Using an actual x86-64 PC is more useful. â†Šī¸Ž