~derf / ref / Running Linux on a Readonly Filesystem
dark mode

To increase flash lifetime and avoid problems with unexpected power cuts, I run all of my embedded Linux systems from a readonly root filesystem. I find this to be easier than setting up an overlayfs.

This page lists all useful or necessary tweaks I know about. I intend to update it every now and then.

Filesystems

Obviously, /etc/fstab needs to specify that / be mounted readonly. /tmp and /var/log must always be writable in my case, so they each get a tmpfs. Depending on your distribution, this might already be the case -- otherwise, you'll probably need something along the following lines.

PARTUUID=12345678-02  /  ext4  defaults,noatime,ro  0  1
tmpfs  /tmp  tmpfs  auto,noatime  0  0
tmpfs  /var/log  tmpfs  auto,noatime,mode=0755  0  0

Note that tmpfs uses half the available memory as size by default.

Software

systemd

PrivateDevices=yes and PrivateTmp=yes do not work on a readonly filesystem. This affects services such as systemd-timesyncd and can be remedied by running systemctl edit systemd-timesyncd and pasting the following lines.

[Service]
PrivateDevices=no
PrivateTmp=no

You may also need to create tmpfs mountpoints for state files, e.g. for systemd-timesyncd:

tmpfs  /var/lib/systemd/timesync  tmpfs  auto,noatime,size=5m,mode=0755,uid=100,gid=103  0  0

Replace 103/100 with UID/GID of your systemd-timesync user.

logrotate

logrotate keeps state in /var/lib/logrotate and may not work if it can't update it. /etc/fstab:

tmpfs  /var/lib/logrotate  tmpfs  auto,noatime,size=1m,mode=0755  0  0

mpd

mpd won't start if /var/log/mpd doesn't exist, so it must be created after mounting the tmpfs on /var/log. It may also be necessary to create /run/mpd. Create /etc/tmpfiles.d/munin-node.conf with the following content:

d /var/log/mpd 0755 mpd audio - -
d /run/mpd 0755 mpd audio - -

Recent mpd versions also appear to write to /var/lib/mpd periodically:

tmpfs /var/lib/mpd tmpfs auto,noatime,size=10m,mode=0755,uid=112,gid=29 0 0

Replace 112/29 with the UID of mpd and the GID of audio

munin-node

munin-node keeps plugin state in /var/lib/munin-node/plugin-state. If you're lazy, you can symlink that to /tmp, but a tmpfs is a better solution. /etc/fstab:

tmpfs  /var/lib/munin-node/plugin-state  tmpfs  auto,noatime,mode=0755  0  0

Also, munin-node will fail if /var/log/munin-node doesn't exist, so it must be created after mounting the tmpfs on /var/log. /etc/tmpfiles.d/munin-node.conf:

d /var/log/munin 0755 munin adm - -

ntp

ntpd stores state in /var/lib/ntp, so it should be a tmpfs. /etc/fstab:

tmpfs  /var/lib/ntp  tmpfs  auto,noatime,size=1m,mode=0755,uid=106,gid=111  0  0

Replace 106/111 with UID/GID of your ntp user.

Note that this does not apply if you use ntpdate instead of ntp.

pulseaudio

pulseaudio has runtime state in /var/lib and ~/.config/pulse (which contains a symlink to a cookie in /tmp that is updated occasionally). The latter can be remedied by setting XDG_CONFIG_HOME for all pulseaudio processes, though I prefer using tmpfs for both. /etc/fstab:

tmpfs  /var/lib/pulse  tmpfs  auto,noatime,size=1m,uid=110,gid=114,mode=0700  0  0
tmpfs  /home/spotifyd/.config/pulse  tmpfs  auto,noatime,size=1m,uid=1001,gid=1001,mode=0700  0  0

Replace 110/114 with UID/GID of your pulse user and 1001 with UID/GID of each user using pulseaudio.

rsyslog

Some of my readonly systems have a tmpfs on /var/spool/rsyslog, though I'm not sure how important that is. Anyways, the following /etc/fstab line may help:

tmpfs  /var/spool/rsyslog  tmpfs  auto,noatime,mode=0755  0  0

sudo

sudo stores the timestamp of the last successful authentication in /var/lib/sudo, which should be writable. /etc/fstab:

tmpfs  /var/lib/sudo  tmpfs  auto,noatime,size=1m,mode=0700  0  0