Reviving an old laptop with zram and Alpine Linux

Recently I dug up an old laptop and figured out I should put it to some use. It is from 2017, although it was slow even by standards of the time when it was manufactured. Windows had become pretty much unusable on it, most likely due to the dying HDD and lack of ram (only 4GB). My goal was extend the life of the disk as much as possible, by keeping it idle 99% of the time. I picked Alpine Linux since I was somewhat familiar with it and it fit all my requirements, including being good as a base for highly customized setups.

There is not much to say about the installation, just follow the official installation guide. I picked the "sys" mode which is a standard disk installation. I could have used "data" or "diskless" mode, but I wanted more control over the disk setup and my goals are slightly different than what the default diskless mode provides. After installation I added my dotfiles and also a couple of packages that I want as part of the base system - Xorg, gcc, gdb, tmux, etc.

Zram as root filesystem

As I said, the HDD was not in a very good shape, so one of my goals was to set it up in a way that it would work purely from RAM - all the files would be copied to RAM on startup and stay there until shutdown. While Linux file cache is pretty great, relying on it results in quite a few additional disk spin ups, which I want to avoid completely. After installing the necessary packages for development my root filesystem had grown to 1GB. With a desktop environment and browser it takes approximately 2GB. In order to best utilize whatever RAM I had, I chose to use zram as my storage medium. Here is the startup script which I use to copy everything:

#!/bin/sh
if [ -n "$(ls /ramdisk)" ]; then
	echo "/ramdisk is not empty, refusing to copy" >&2
	exit 1
fi
# If any command fails, abort immediately
set -ex
mkdir -p /ramdisk /realroot
# zram module is not loaded by default, although you can add the line "zram" to /etc/modules to do it on boot
# either way, no harm in calling modprobe twice
modprobe zram
# Create the block device with maximum (uncompressed) data size 2G and algorithm "zstd"
ZRAM="$(zramctl -f -s 2G -a zstd)"
[ -n "$ZRAM" ]
# Format it as ext4 filesystem
mkfs.ext4 "$ZRAM"
# Mount options optimized for performance at the expense of reliability
# (no point in making a ramdisk robust against disk failure, as it is erased anyway)
mount -o data=writeback,commit=600,journal_async_commit,barrier=0 "$ZRAM" /ramdisk
# List of directories from root filesystem that we want to move to RAM
COPY="bin etc home lib opt root run sbin srv usr var"
for d in $COPY; do
	cp -a "/$d" /ramdisk
	mkdir -p /realroot/"$d"
	# Make original directory accessible through /realroot in case we want to modify it easily
	mount -o bind /"$d" /realroot/"$d"
	# Shadow the original directory path with the copy which is in ramdisk
	mount -o nonempty,bind /ramdisk/"$d" /"$d"
done

The script will shadow the original directories with their copies in RAM using bind mounts. Strictly speaking, this script should be integrated as part of initramfs, before any services are started, but in practice all the services I start on boot do not keep open file handles to the original root filesystem, so there is no harm in having this script run as a normal startup service. In my case it takes roughly a minute to copy all the files, which for me is acceptable as I don't intend to boot very often. After the initial copy, the laptop feels very snappy, there is zero delay for searching through directories. The Zstandard algorithm is doing a very good job, compressing a 1GB filesystem into 500MB.

TODO

Copying the files is not very robust, for example when a named pipe is copied, it results in creating a new, unrelated named pipe. As long as no complex services are started before this script, there aren't any issues. Also it could be faster to directly copy the root filesystem block device into zram instead of doing a recursive file copy. Ideally it would be possible to completely unmount the HDD after copying to RAM, but the current implementation with bind mounts does not support this.

I was also looking into the option to sync changes back into the HDD, but there are some challenges. Maybe an additional RAM overlay filesystem could be used, but so far I did not find any way to easily apply overlayfs changes to a filesystem which is not the one being overlayed as is the case here (we want to apply changes to the HDD, not the zram root filesystem which would be under the overlay in this case). For now I chose to just copy any files which I want to persist manually, using the bind mounts under /realroot.

It's also possible that ext4 is not the best option for a zram device, although I did not find any info regarding this, except to use the mentioned mount options to trade reliability for speed.