Error: Invalid Frontmatter

Path: /is/htdocs/wp11060373_G0UUW3NADW/www/user/pages/02.b/armbian-readonly-root/

Failed to read /is/htdocs/wp11060373_G0UUW3NADW/www/user/pages/02.b/armbian-readonly-root/ A YAML file cannot contain tabs as indentation at line 5 (near " - armbian").

title: 'Armbian with read-only root file system'
date: 17.04.2020
        - armbian
        - banana pro

A big problem that most users of single-board computers face is the limited
number of write cycles that SD-cards can endure. Constantly recurring
writes to log files and caches wear out the flash memory until eventually
write operations fail and the system crashes. This is on how to set up the
root parition as 'read-only' on [Armbian](

## The initial situation
I am starting out with a fresh installation of Armbian Buster, version
`2020-02-17`, kernel 5.4 (a variant of Debian.) My device is a [LeMaker
Banana Pro](, but I am quite confident that it should work on other platforms, too. I use a
CP210x USB UART Bridge to connect my PC to the serial port of the
device, just in case it won't be able to connect to the network any more.
I also set `verbosity=8` in `/boot/armbianEnv.txt` to be able to see
kernel and systemd log messages.

## Network configuration
I never use NetworkManager on headless systems, because I suspect it to
make the network connection unstable. Of course, I tried to configure
read-only root with NetworkManager, but the problem is that it tries to
create a temporary file with a random name in `/var/lib/NetworkManager`.
So remove the package, and configure your interfaces in
`/etc/network/interfaces`. Example:

source /etc/network/interfaces.d/*

auto lo iface lo inet loopback

auto eth0 iface eth0 inet dhcp

The problem I ran into is that this version of Armbian has resolvconf
configured to resolve all domain names via Cloudflare's nameserver, which
doesn't work for me. So remove the corresponding line from
`/etc/resolvconf/resolv.conf.d/head`. Finally, `/etc/resolv.conf` must be a
symlink to `/etc/resolvconf/run/resolv.conf`.

## The new filesystem layout
If you take a look at the Filesystem Hierarchy Standard[5], you can see
that the following locations must be writeable:

* `/tmp` is already mounted as tmpfs on Armbian, so there is nothing to do here.
* `/run` shall be deleted on boot anyway, so we will mount it as tmpfs,
* Actually, the entire `/var` directory is for writeable files, but we will
  make it read-only except for some individual directories:
* I will ignore `/var/cache`, as it is only written to when someone is
  actively using the system.
* `/var/lock/` is a symlink to `/run/lock` already.
* `/var/run` is linked to `/run`.
* `/var/tmp` is supposed to be deleted less often than `/tmp` and not at
  reboot, but we will be fine linking it to `/tmp`.
* `/var/log` is mounted as zram by the armbian-zram-config service.
* `/var/spool` is used by cron and rsyslog.
* Some packages need to write files in `/etc` and `/var/lib`. I will
  handle these individually.

## Removing conflicting packages and services
Some packages are unnecessary or will simply lose their purpose on the
read-only system. The new system will not write logs to permanent storage
any more, so they need not be rotated any more. It might also be worth to
think about replacing rsyslog by busybox-syslog, which does not write
logfiles at all, like in [1]. On the other hand, you could keep logrotate
and configure it to write logs to a USB-stick, for example. I will also
disable automatic software upgrades. `systemd-rfkill` writes the rfkill
state to `/var/lib`, but as the wlan module is unblocked by default, I
will remove the service.

apt autoremove --purge unattended-upgrades logrotate systemctl mask apt-daily.service systemctl mask systemd-rfkill.service

## Setting the kernel to read-only boot
Add `extraargs+=ro` to `/boot/armbianEnv.txt`. This kernel option
instructs the kernel to mount the root partition as read-only. [1] also
sets `fastboot` and `noswap`, but these are specific to Debian with
SystemV-style init.

## The fstab
In `/etc/fstab`, add the `ro` mount option to your root partition. Mount
`/run` as tmpfs.  Some suggestions on mount options can be found under
[2]. My fstab looks like this:

UUID=<...> / ext4 defaults,ro,noatime,nodiratime,commit=600,errors=remount-ro 0 1 tmpfs /tmp tmpfs nodev,nosuid,size=20%,mode=1777 0 0 tmpfs /run tmpfs nodev,nosuid,size=10%,mode=755 0 0

## Setting up tmpfiles
I need to create some files in `/tmp` on every boot. I will symlink to
them later. Create a configuration file
`/etc/tmpfiles.d/000readonly-root.conf` with the following content:

d /tmp/var 0755 - - - - d /tmp/var/spool 0755 - - - - d /tmp/var/tmp 1777 - - - - d /tmp/var/lib 0755 - - - - d /tmp/var/lib/dhcp 0755 - - - -

For the sake of completeness, you can also add the symlinks there, which I
will add manually:

L /var/spool - - - - /tmp/var/spool L /var/tmp - - - - /tmp/var/tmp L /var/lib/dhcp - - - - /tmp/var/lib/dhcp

On raspbian, dhcpcd5 is used[1]. If you use dhcpcd5, create corresponding
temporary directories and symlinks.

Copy `/usr/lib/tmpfiles.d/var.conf` to `/etc/tmpfiles.d/var.conf`. The
copy will override the former.

## Moving directories and symlinking to `/tmp`
Having written the tempfiles configuration, `systemctl start
systemd-tmpfiles-setup.service` to create those directories. (Re-)move the
old directories and create the symlinks:

rm -rf /var/lib/dhcp ln -s /tmp/var/lib/dhcp /var/lib/dhcp

mv /var/spool/* /tmp/var/spool rm -d /var/spool ln -s /tmp/var/spool /var/spool

rm -rf /var/tmp ln -s /tmp/var/tmp /var/tmp

If you have not linked `/etc/resolv.conf` to
`/etc/resolvconf/run/resolv.conf` yet, do so now:

rm -f /etc/resolv.conf ln -s /etc/resolvconf/run/resolv.conf /etc/resolv.conf


Previous Post