Read-only Filesystem for Raspberry Pi
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!
Update: Still works with a Raspberry Pi 1 and Raspbian Stretch Lite (2018-04-18).
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.
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 | # The modules "most" classes added per default to the initramfs
auto_add_modules()
{
local arg
local modules=
if [ "$#" -eq 0 ] ; then
set -- base net ide scsi block ata i2o dasd ieee1394 firewire mmc usb_storage
fi
for arg in "$@" ; do
case "$arg" in
base)
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.
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | 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
else
mount ${roflag} ${ROOTFLAGS} ${ROOT} /lower
fi
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). Now you can run update-initramfs using the kernel version. Then you just rename the resulting file.
Update: the command below takes care of determining the correct Kernel version.
update-initramfs -c -k "$(uname -r)"
mv "/boot/initrd.img-$(uname -r)" /boot/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:
55 56 57 58 59 | # Enable audio (loads snd_bcm2835)
dtparam=audio=on
kernel=kernel.img
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:
55 56 57 58 59 | # Enable audio (loads snd_bcm2835)
dtparam=audio=on
kernel=kernel7.img
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.
Finish
If you have followed all the steps you can now reboot. After reboot run df to see if it worked.
df
# 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.