Read-only Filesystem for Raspberry Pi

without comments

I recently set up a Raspberry Pi as a print- and scan-server. I want to be able to just turn off the Raspberry Pi without a proper shutdown, so to prevent SD-card corruption a read-only filesystem was needed. The easy way to implement this is to just mount the root filesystem as read-only. An unwanted side-effect of this is that all writes to the filesystem fail. The superior solution uses a read-only root with an overlay stored in RAM. Luckily ejolson on the Raspberry Pi forum has written a nice tutorial to implement this.

I successfully configured Raspbian Jessie Lite (2016-11-25) running on a Raspberry Pi 1. I have not tested it with any other Pi, but they should work just as well. If you try this tutorial with a more recent Raspbian version it might not work!

Step 1

Change into a root shell and change to /usr/share/initramfs-tools by typing:

sudo bash
cd /usr/share/initramfs-tools # type into root shell

You have to change the file hook-functions. Just add overlay to the modules in line 528.

# The modules "most" classes added per default to the initramfs
        local arg
        local modules=

        if [ "$#" -eq 0 ] ; then
                set -- base net ide scsi block ata i2o dasd ieee1394 firewire mmc usb_storage

        for arg in "$@" ; do
                case "$arg" in
                        modules="$modules ehci-pci ehci-orion ehci-hcd ohci-hcd ohci-pci uhci-hcd usbhid overlay"
                        modules="$modules xhci xhci-pci xhci-hcd"
                        modules="$modules btrfs ext2 ext3 ext4 ext4dev "
                        modules="$modules isofs jfs reiserfs udf xfs"
                        modules="$modules nfs nfsv2 nfsv3 nfsv4"
                        modules="$modules af_packet atkbd i8042 psmouse"
                        modules="$modules virtio_pci virtio_mmio"

You can find my version on Gist: /usr/share/initramfs-tools/hook-functions.

This takes care of loading the overlay module at boot time.

Step 2

Next, you have to create a new boot script. Change to /usr/share/initramfs-tools/scripts and copy the existing boot script. Don’t worry if the last command fails, it will still work.

cd /usr/share/initramfs-tools/scripts
cp local overlay
cp -rp local-premount overlay-premount
cp -rp local-bottom overlay-bottom # don't mind if this fails

Now you’ll have to change the file overlay to mount the root read-only. The changes start in line 138.

	ROOT=$(resolve_device "$ROOT")

#	if [ "${readonly}" = "y" ]; then
#		roflag=-r
#	else
#		roflag=-w
#	fi

	# FIXME This has no error checking
	modprobe ${FSTYPE}

	checkfs ${ROOT} root

	# FIXME This has no error checking
	# Mount root
#	if [ "${FSTYPE}" != "unknown" ]; then
#		mount ${roflag} -t ${FSTYPE} ${ROOTFLAGS} ${ROOT} ${rootmnt}
#	else
#		mount ${roflag} ${ROOTFLAGS} ${ROOT} ${rootmnt}
#	fi
	mkdir /upper /lower
	if [ "${FSTYPE}" != "unknown" ]; then
		mount ${roflag} -t ${FSTYPE} ${ROOTFLAGS} ${ROOT} /lower
		mount ${roflag} ${ROOTFLAGS} ${ROOT} /lower
	modprobe overlay
	mount -t tmpfs tmpfs /upper
	mkdir /upper/data /upper/work
	mount -t overlay \
		-olowerdir=/lower,upperdir=/upper/data,workdir=/upper/work \
		overlay ${rootmnt}

Gist: /usr/share/initramfs-tools/scripts/overlay

Step 3 (Raspberry Pi 1, Zero, B+)

You can now generate a suitable initramfs. However, you’ll have to determine the kernel version first. In this example it is 4.4.34+. For a Raspberry Pi 2 or later it would be 4.4.34-v7+ (notice the v7). Then you can run update-initramfs using the kernel version. Then you just rename the resulting file.

uname -a
# Linux raspberrypi 4.4.34+ #930 Wed Nov 23 15:12:30 GMT 2016 armv6l GNU/Linux
update-initramfs -c -k 4.4.34+
cd /boot
mv initrd.img-4.4.34+ initrd.img

Now you have to tell the bootloader to load the initramfs. This can be done by adding the following lines to /boot/config.txt:

# Enable audio (loads snd_bcm2835)

initramfs initrd.img

Gist: /boot/config.txt

Step 3 (Raspberry Pi 2 and later)

As the later Raspberry Pi models use a different CPU, the output of uname will have an additional v7 in the kernel version (e.g. 4.4.34-v7+). The commands change to:

uname -a
# Linux raspberrypi 4.4.34-v7+ #930 Wed Nov 23 15:12:30 GMT 2016 armv6l GNU/Linux
update-initramfs -c -k 4.4.34-v7+
cd /boot
mv initrd.img-4.4.34-v7+ initrd7.img

Notice the 7 in initrd7.img. Now change /boot/config.txt:

# Enable audio (loads snd_bcm2835)

initramfs initrd7.img

Step 4

The last step is to change the boot command. Just add boot=overlay to the command in /boot/cmdline.txt:

boot=overlay dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait fastboot noswap

To disable the overlay this string has to be removed. As this file is found in the FAT-formatted boot partition this can be done by inserting the SD-card into a Windows PC as well. Notice that I have also added fastboot and noswap to the command. These options disable filesystem checking at boot and the swap.


If you have followed all the steps you can now reboot. After reboot run df to see if it worked.

# Filesystem     1K-blocks  Used Available Use% Mounted on
# udev               10240     0     10240   0% /dev
# tmpfs              37092  4596     32496  13% /run
# overlay            92720  1180     91540   2% /
# tmpfs              92720     0     92720   0% /dev/shm
# tmpfs               5120     4      5116   1% /run/lock
# tmpfs              92720     0     92720   0% /sys/fs/cgroup
# /dev/mmcblk0p1     64456 26528     37928  42% /boot
# tmpfs              18548     0     18548   0% /run/user/1000

If the filesystem for root (/) shows up as overlay it worked.

Written by Ferdinand

Januar 15th, 2017 at 15:35

Posted in Raspberry Pi

Tagged with ,

Leave a Reply