The Router Project — Part 2: Setting up alpine linux
Sep 6, 2017
7 minute read

This is part 2 of the router series; you can read part 1 here

The second part on the router series will deal with setting up alpine linux which will form the basis of our router software stack. You do not necessarily need to use alpine for the operating system however its lightweight nature along with the run-from-RAM capability really sets it apart and I think it’s an ideal fit for this project. If you are not interested skip directly to part 3.

Alpine linux has the capability to load configuration on boot. When it boots from USB or other removable media it will load up any extra packages as well all the saved configuration that is stored on a hard disk, SD card, CF card, you name it! Previous configurations can be backed up which makes rolling back the system as easy as renaming a file. You can always do a traditional disk installation if you are so inclined but this is beyond the scope of this article. The installation guide is fairly detailed however there are some issues that you might need to address.

First of all the bootable medium does not forward console output to a serial port. This is not a problem if you have a monitor that you can plug in but if you are relying on serial port for interaction as is the case in PC Engines boards you will need to modify the installation medium to output to serial console. Then you can follow the installation guide in diskless mode as usual. You can edit the iso image directly using an ISO editing tool however I have hacked together a small script that does all the work for you. As this script uses systemd-nspawn to setup a minimal alpine environment you will need a systemd-based distribution to run it. Running it is as easy as

# ./make-bootable-alpine.sh RELEASE [TARGET]

So if you want to burn version 3.6.2 on /dev/sdd (your USB drive) just run

# ./make-bootable-alpine.sh 3.6.2 /dev/sdd

Please be extra careful when providing the device argument as it will delete everything on your thumb drive. This script is also useful for upgrading alpine to a different release. Just change the release version and you are done! By default it will forward all output to /dev/ttyS0 but if you need a different serial port edit line 136 to your desired value.

Either you edited the bootable medium or not pop it in your computer and the alpine live environment should boot up. Before setting up alpine we need to set up network and repositories. To do so run setup-alpine and follow the on-screen instructions up to the point of repository selection. Afterwards just hit Ctrl-C. We will do the rest of the setup in a minute. Just use the dhcp option for the interface that is connected to the internet. We will not do any router specific configuration at the moment, since this article is oriented into setting up alpine.

Now we need to setup our persistence medium. This is a device that will store all packages and local configuration that will load up on every boot. For the PC engines build I used an SD card which is presented to the operating system under /dev/mmcblk0 but you can use any hard disk or USB flash you have around. Then you need to format it. I think that ext4 usually works best with alpine. You can probably use any filesystem but I found that alpine would refuse to properly mount a device formatted in YAFS or JFFS for example. So stick with what works. This is probably an initramfs issue as there is no particular reason to refuse to mount any other filesystem as far as the kernel is concerned but it I find it too much of a hassle to hack around the initrd scripts.

You will probable need to install e2fsprogs in order to have the mkfs.ext4 script available. To do so run

# apk add e2fsprogs

We can now format the persistence medium. I do not use a partition, formatting instead the whole device, but if you need partitions feel free to do so. All relevant tools such as fdisk and parted are available through apk.

# mkfs.ext4 -O ^has_journal /dev/mmcblk0

The ^has_journal bit is recommended for SD cards as it will minimise writes to preserve the SD card lifetime. Since writes will be infrequent and the filesystem will be mounted most of the time as read-only we can relax the requirement for a journal. If you are using a regular disk or SSD you can safely forgo this option.

You can now mount the newly created filesystem. I recommend that you use the device name instead of a custom name. For example if the formatted device is sda1 mount it under /media/sda1. It seems that alpine’s initrd scripts mount every available filesystem under /media using the device name, ignoring your fstab. This kind of makes sense since fstab is not available in the pre-boot environment. So in order to avoid any further complications I recommend that we stick to that convention.

Before running setup-alpine for the last time we should take care of a few config files. First add the persistence medium in your fstab like so

/dev/mmcblk0    /media/mmcblk0  ext4    ro,noatime,data=writeback,acl  0 0

Since we only need to read from the persistence medium the ro option is recommended, especially for SD cards where journal is usually not available. Remove data=writeback if you have a journal on your device.

Next up is /etc/lbu/lbu.conf. lbu is alpine’s backup utility which is also used for saving the current configuration. You might want to uncomment the BACKUP_LIMIT and setting up to a sensible number. Every time you save your configuration a backup will be kept in a round robin fashion.

We should now remove the live media as a repository. This will allow us to only fetch up-to-date packages from the remote repository. To do so edit /etc/apk/repositories and remove any local media.

Finally create a folder on your persistence medium to cache the downloaded packages. All installed packages will be first loaded from the cache and then from the remote repository. This is important as packages will be installed again EVERY time the computer boots. So we should not download them again.

# mkdir /media/mmcblk0/cache

You can now run the setup-alpine script following the details on the wiki. When you are asked where to save config files choose mmcblk0 or the device we formatted earlier. Same thing for the cache directory.

Before rebooting we should upgrade the system packages to the latest version. To do so

# apk update && apk upgrade

You can now edit around whatever you want in the /etc folder. When you are done you would probably want to save your changes. To do so the lbu utility should be used. lbu has a series of useful commands and I recommend that you read its wiki page carefully. In its simplest form lbu will save your configuration with

# lbu commit

This command must be run EVERY time you change something to your system, like installing a package or updating a configuration file. If you do not do so all your changes will be LOST on the next boot. By default lbu only saves files and folders under /etc. If you need to save other folders, for example /usr/local where your custom programs or scripts reside you should add them to the list by using

# lbu add /usr/local && lbu commit

If you also have non-root users you should probably check their home folders into lbu as well. Please note that you do not need to remount your persistence medium as rw every time you run lbu. It will take care of that and remount it back as ro when it exits.

So that’s basically how to get started with alpine. If everything went well you can reboot the computer and all your changes should be loaded up on boot and all local packages from the cache folder will be reinstalled.

That’s the end of part 2. In part 3 we will start to look into router-specific configuration.