About me: My name is Solène Rapenne, pronouns she/her. I like learning and sharing knowledge. Hobbies: '(BSD OpenBSD Qubes OS Lisp cmdline gaming security QubesOS internet-stuff). I love percent and lambda characters. OpenBSD developer solene@. No AI is involved in this blog.

Contact me: solene at dataswamp dot org or @solene@bsd.network (mastodon).

I'm a freelance OpenBSD, FreeBSD, Linux and Qubes OS consultant, this includes DevOps, DevSecOps, technical writing or documentation work. If you enjoy this blog, you can sponsor my open source work financially so I can write this blog and contribute to Free Software as my daily job.

How-to install Alpine Linux in full ram with persistency

Written by Solène, on 14 July 2023.
Tags: #immutability #linux #alpine

Comments on Fediverse/Mastodon

1. Introduction §

In this guide, I'd like to share with you how to install Alpine Linux, so it runs entirely from RAM, but using its built-in tool to handle persistency. Perfect setup for a NAS or router, so you don't waste a disk for the system, and this can even be used for a workstation.

Alpine Linux official project website

Alpine Linux wiki: Alpine local backup

2. The plan §

Basically, we want to get the Alpine installer on a writable disk formatted in FAT instead of a read only image like official installers, then we will use the command lbu to handle persistency, and we will see what need to be configured to have a working system.

This is only a list of steps, they will be detailed later:

  1. boot from an Alpine Installer (if you are using Alpine, you don't need too)
  2. format an usb memory drive with an ESP partition and make it bootable
  3. run setup-bootloader to copy the bootloader from the installer to the freshly formatted drive
  4. reboot on the usb drive
  5. run setup-alpine
  6. you are on your new Alpine system
  7. run lbu commit to make changes persistent across reboot
  8. make changes, run lbu commit again
A mad scientist Girl with a t-shirt labeled "rare t-shirt" is looking at a penguin strapped on a Frankenstein like machine, with his head connected to a huge box with LBU written on it.
A mad scientist Girl with a t-shirt labeled "rare t-shirt" is looking at a penguin strapped on a Frankenstein like machine, with his head connected to a huge box with LBU written on it.

Artwork above by Prahou

3. The setup §

3.1. Booting Alpine §

For this step you have to download an Alpine Linux installer, take the one that suits your needs, if unsure, take the "Extended" one. Don't forget to verify the file checksum.

Once you have the ISO file, create the installation media:

Alpine Linux documentation: Using the image

Now, boot your system using your brand-new installer.

3.2. Writable boot media creation §

In this step, we will need to boot on the Alpine installer to create a new Alpine installer, but writable.

You need another USB media for this step, the one that will keep your system and data.

On Alpine Linux, you can use setup-alpine to configure your network, key map and a few things for the current system. You only have to say "none" when you are asked what you want to install, where, and if you want to store the configuration somewhere.

Run the following commands on the destination USB drive (networking is required to install a package), this will format it and use all the space as a FAT32 partition. In the example below, the drive is /dev/sdc.

apk add parted
parted /dev/sdc -- mklabel gpt
parted /dev/sdc -- mkpart ESP fat32 1MB 100%
parted /dev/sdc -- set 1 esp on

This creates a GPT table on /dev/sdc, then creates a first partition as FAT32 from the first megabyte up to the full disk size, and finally marks it bootable. This guide is only for UEFI compatible systems.

We actually have to format the drive as FAT32, otherwise it's just a partition type without a way to mount it as FAT32:

mkfs.vfat /dev/sdc1
modprobe vfat

Final step, we use an Alpine tool to copy the bootloader from the installer to our new disk. In the example below, your installer may be /media/usb and the destination /dev/sdc1, you could figure the first one using mount.

setup-bootable /media/usb /dev/sdc1

At this step, you made a USB disk in FAT32 containing the Alpine Linux installer you were using live. Reboot on the new one.

3.3. System installation §

On your new installation media, run setup-alpine as if you were installing Alpine Linux, but answer "none" when you are asked which disk you want to use. When asked "Enter where to store configs", you should be prompted your new device by default, accept. Immediately, after, you will be prompted for an APK cache, accept.

At this point, we can say Alpine is installed! Don't reboot yet, you are already on your new system!

Just use it, and run lbu commit when you need to save changes done to packages or /etc/. lbu commit creates a new tarball in your USB disk containing a list of files configured in /etc/apk/protected_paths.d/, and this tarball is loaded at boot time, and will install your package list quickly from the local cache.

Alpine Linux wiki: Alpine local backup (lbu command documentation)

Please take extra care that if you include more files, everything you commit the changes, they have to be stored on your USB media. You could modify the fstab to add an extra disk/partition for persistent data on a performant drive.

4. Updating the kernel §

The kernel can't be upgraded using apk, you have to use the script update-kernel that will create a "modloop" file in the boot partition which contains the boot image. You can't rollback this file.

You will need a few gigabytes in your in-memory filesystem, or use a temporary build directory by affecting TMPDIR variable to a persistent storage.

By default, tmpfs on root is set to 1 GB, this can be increased given you have enough memory using the command: mount -o remount,size=6G /.

The script should have the boot directory as a parameter, so it should look like update-kernel /media/usb/boot in a default setup, if you use an external partition, this would look like env TMPDIR=/mnt/something/ update-kernel /media/usb/boot.

4.1. Extra configuration §

Here is a list of tweaks to improve your experience!

4.1.1. keep last n configuration §

By default, lbu will only keep the last version you save, by settingBACKUP_LIMIT to a number n, you will always have the last n versions of your system stored in the boot media, this is practical if you want to roll back a change.

4.1.2. apk repositories §

Edit /etc/apk/repositories to uncomment the community repository.

4.1.3. fstab check §

Edit /etc/fstab to make sure the disk you are using is explicitly configured using a UUID entry, if you only have this:

/dev/cdrom	/media/cdrom	iso9660	noauto,ro 0 0
/dev/usbdisk	/media/usb	vfat noauto,ro 0 0

This mean your system may have troubles if you use it on a different computer or that you plug another USB disk in it. Fix by using the UUID of your partition, you can find it using the program blkid from the eponym package, and fix the fstab like this:

UUID=61B2-04FA	/media/persist	vfat	noauto,ro 0 0
/dev/cdrom	/media/cdrom	iso9660	noauto,ro 0 0
/dev/usbdisk	/media/usb	vfat noauto,ro 0 0

This will ALWAYS mount your drive as /media/persist.

If you had to make the change, you need to make some extra changes to keep things coherent:

  • set LBU_MEDIA=persist into /etc/lbu/lbu.conf
  • umount the drive in /media and run mkdir -p /media/persist && mount -a, you should have /media/persist with data in it
  • run lbu commit to save the changes

4.1.4. desktop setup §

You can install a graphical desktop, this can easily be done with these commands:

setup-desktop xfce
setup-xorg-base

Due to a bug, we have to re-enable some important services, otherwise you would not have networking at the next boot:

rc-update add hwdrivers sysinit

Alpine bug report #9653

You may want to enable the display manager at boot, which may be lightdm, gdm or sddm depending on your desktop:

rc-update add lightdm

4.1.5. user persistency §

If you added a user during setup-alpine, its home directory has been automatically added to /etc/apk/protected_paths.d/lbu.list, when you run lbu commit, its whole home is stored. This may not be desired.

If you don't want to save the whole home directory, but only a selection of files/directories, here is how to proceed:

  1. edit /etc/apk/protected_paths.d/lbu.list to remove the line adding your user directory
  2. you need to create the user directory at boot with the correct permissions: echo "install -d -o solene -g solene -m 700 /home/solene" | doas tee /etc/local.d/00-user.start
  3. in case you have some persistency set at least one user sub directories, it's important to fix the permissions of all the user data after the boot: echo "chown -R solene:solene /home/solene | doas tee -a /etc/local.d/00-user.start
  4. you need to mark this script as executable: doas chmod +x /etc/local.d/00-user.start
  5. you need to run the local scripts at boot time: doas rc-update add local
  6. save the changes: doas lbu commit

I'd recommend the use of a directory named Persist and adding it to the lbu list. Doing so, you have a place to store some important data without having to save all your home directory (including garbage such as cache). This is even nicer if you use ecryptfs as explained below.

4.1.6. extra convenience §

Because Alpine Linux is packaged in a minimalistic manner, you may have to install a lot of extra packages to have all the fonts, icons, emojis, cursors etc... working correctly as you would expect for a standard Linux desktop.

Fortunately, there is a community guide explaining each section you may want to configure.

Alpine Linux wiki: Post installation

4.1.7. Set X default keyboard layout §

Alpine insists of you using a qwerty desktop for X until you log into your session, this can be complicated to type passwords.

You can create a file /etc/X11/xorg.conf.d/00-keyboard.conf like in the linked example and choose your default keyboard layout. You will have to create the directories /etc/X11/xorg.conf.d first.

Arch Linux wiki: Keyboard configuration

4.1.8. encrypted personal directory §

You could use ecryptfs to either encrypt the home partition of your user, or just give it a Private directory that could be unlocked on demand AND made persistent without pulling all the user files at every configuration commit.

$ doas apk add ecryptfs-utils
$ doas modprobe ecryptfs
$ ecryptfs-setup-private
Enter your login passphrase [solene]:
Enter your mount passphrase [leave blank to generate one]:
[...]
$ doas lbu add $HOME/.Private
$ doas lbu add $HOME/.ecryptfs
$ echo "install -d -o solene -g solene -m 700 /home/solene/Private" | doas tee /etc/local.d/50-ecryptfs.start
$ doas chmod +x /etc/local.d/50-ecryptfs.start
$ doas rc-update add local
$ doas lbu commit

Now, when you need to access your private directory, run ecryptfs-mount-private and you have your $HOME/Private directory which is encrypted.

You could use ecryptfs to encrypt the whole user directory, this requires extra steps and changes into /etc/pam.d/base-auth, don't forget to add /home/.ecryptfs to the lbu include list.

Using ecryptfs guide

5. Security §

Let's be clear, this setup isn't secure! The weak part is the boot media, which doesn't use secure boot, could easily be modified, and has nothing encrypted (except the local backups, but NOT BY DEFAULT).

However, once the system has booted, if you remove the boot media, nothing can be damaged as everything lives in memory, but you should still use passwords for your users.

6. Conclusion §

Alpine is a very good platform for this kind of setup, and they provide all the tools out of the box! It's a very fun setup to play with.

Don't forget that by default everything runs from memory without persistency, so be careful if you generate data you don't want to lose (passwords, downloads, etc...).

7. Going further §

The lbu configuration can be encrypted, this is recommended if you plan to carry your disk around, especially if it contains sensitive data.

You can use the fat32 partition only for the bootloader and the local backup files, but you could have an extra partition that could be mounted for /home or something, and why not a layer of LUKS for encryption.

You may want to use zram if you are tight on memory, this creates a compressed block device that could be used for swap, it's basically compressed RAM, it's very efficient but less useful if you have a slow CPU.