How to make your Raspberry Pi file system read-only (Raspbian Stretch)

Andreas Schallwig
Jun 10, 2019 · 5 min read

Update: I’ve created a new version of this guide for Raspbian Buster.

Raspberry Pi(e)s are often used for kiosk applications where their sole purpose is to serve preloaded HTML5 content from an SD card to its attached HDMI screen. This is an easy solution to set up, however at some point in time you will encounter weird issues resulting from SD card corruption.

In our case usually after around 6 months we would encounter random crashes and find log messages like this:

end_request: I/O error, dev mmcblk0, sector 148225
mmcblk0: error -110 transfering data, sector 148226, nr 254, response 0x900, card status 0xb00

What is going on here?!

This message basically means your SD card’s file system was damaged. Possible causes include:

  • SD cards were not designed with 24/7 operation in mind. They will accumulate bad sectors rather quickly after some time.
  • Kiosk applications are often not properly shut down but instead simply unplugged. Repeating this increases the risk of file system corruption.

While this can be fixed in some cases it’s usually not worth the trouble (you do have a backup image, don’t you?). In addition, once an SD card starts throwing this kind of errors it’s time to replace it anyway. The more sustainable solution though is to prevent the file system from becoming corrupt entirely.

“But I’m not writing any data to my SD card, it’s just serving some HTML files?”

True, YOUR application may not write any data — still your operating system (Raspbian etc.) or GUI (Chromium / Firefox browser) is constantly writing temporary files, log files, cache files etc. There is a lot going on under the hood and this is what eventually causes your SD card to corrupt in the mid- to long run.

Luckily if you’re using Raspbian Stretch (or any other Linux flavored OS) you can force your file system into “read-only mode” rather easily. While in read-only mode the system cannot write any data to your SD card thus significantly prolonging the lifespan of your card.

To achieve this, we need to take the following steps:

  1. Configure the operating system to write all temporary files to the “tmpfs” file system which resides in memory.
  2. Configure additional services to also use the tempfs file system.
  3. Redirect all system log files to memory.
  4. Add some scripts to conveniently toggle read-only mode on / off (Optional)

Let’s get started

Important: You absolutely should create a backup image of your SD card before following this tutorial. I have used this method successfully on countless Pi(e)s, still you use this at your own risk. Don’t blame me if anything goes wrong 😉

Preparations

Log in to your Raspberry via SSH. I recommend you first update your Pi one more time to get all the latest software packages:

$ sudo apt-get update && apt-get upgrade

Afterwards do a little housekeeping and remove the following packages. If these were not installed in the first place then just skip this step:

$ sudo apt-get remove --purge wolfram-engine triggerhappy anacron logrotate dphys-swapfile xserver-common lightdm

Stop the X11 server (if running):

$ sudo systemctl disable x11-common

Clean up your packages:

$ sudo apt-get autoremove --purge

Remove some startup scripts

$ sudo systemctl disable bootlogs
$ sudo systemctl disable console-setup

Replace your log manager

We want to avoid writing any system log files to the SD Card. Therefore we will remove the standard syslog output of log files to /var/log and instead replace it with the busybox in-memory logger:

$ sudo apt-get install busybox-syslogd
$ sudo dpkg --purge rsyslog

Disable swap and filesystem check and set it to read-only

Edit the file /boot/cmdline.txt and add the following three words at the end of the line: fastboot noswap ro

Example:

dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait fastboot noswap ro

Move some system files to temp filesystem

$ sudo rm -rf /var/lib/dhcp /var/lib/dhcpcd5 /var/run /var/spool /var/lock /etc/resolv.conf
$ sudo ln -s /tmp /var/lib/dhcp
$ sudo ln -s /tmp /var/lib/dhcpcd5
$ sudo ln -s /tmp /var/run
$ sudo ln -s /tmp /var/spool
$ sudo ln -s /tmp /var/lock
$ sudo touch /tmp/dhcpcd.resolv.conf
$ sudo ln -s /tmp/dhcpcd.resolv.conf /etc/resolv.conf

Move some lock files

Edit the file /etc/systemd/system/dhcpcd5 and change the line:

PIDFile=/run/dhcpcd.pid

to:

PIDFile=/var/run/dhcpcd.pid

Update the systemd random seed

Link the random-seed file to the tmpfs location:

$ sudo rm /var/lib/systemd/random-seed
$ sudo ln -s /tmp/random-seed /var/lib/systemd/random-seed

Edit the service configuration file /lib/systemd/system/systemd-random-seed.service to have the file created on boot. Add the line ExecStartPre=/bin/echo "" >/tmp/random-seed under the [Service] section.

Example:

[Service]Type=oneshot
RemainAfterExit=yes
ExecStartPre=/bin/echo "" >/tmp/random-seed
ExecStart=/lib/systemd/systemd-random-seed load
ExecStop=/lib/systemd/systemd-random-seed save

Finally reload the systemd service:

$ sudo systemctl daemon-reload

Make the file-systems read-only

Update the file /etc/fstab and add the ,ro flag to all block devices

Example:

proc            /proc           proc    defaults             0       0
/dev/mmcblk0p1 /boot vfat defaults,ro 0 2
/dev/mmcblk0p2 / ext4 defaults,noatime,ro 0 1

Add the entries for the temporary file system at the end of the file:

tmpfs           /tmp            tmpfs   nosuid,nodev         0       0
tmpfs /var/log tmpfs nosuid,nodev 0 0
tmpfs /var/tmp tmpfs nosuid,nodev 0 0

Optional: Adding some fancy commands to switch between RO and RW modes

This step is optional but adds a lot of convenience in case you need to switch between read-only and read-write modes for debugging or updating. It will provide two simple shell commands “ro” (read-only) and “rw” (read-write) which can be used at any time to switch between the modes. In addition it will add an indicator to your command prompt to show the current mode.

Edit the file /etc/bash.bashrc and add the following lines at the end:

set_bash_prompt() {fs_mode=$(mount | sed -n -e "s/^\/dev\/.* on \/ .*(\(r[w|o]\).*/\1/p")PS1='\[\033[01;32m\]\u@\h${fs_mode:+($fs_mode)}\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '}alias ro='sudo mount -o remount,ro / ; sudo mount -o remount,ro /boot'alias rw='sudo mount -o remount,rw / ; sudo mount -o remount,rw /boot'PROMPT_COMMAND=set_bash_prompt

Finally ensure the file system goes back to read-only once you log out. Edit (or create) the file /etc/bash.bash_logout and add the following lines at the end:

mount -o remount,ro /
mount -o remount,ro /boot

You’re done! Now just reboot the system:

$ sudo reboot

Andreas Schallwig

Written by

Co-founder of multiple IT start-ups, passionate about blinking stuff, computers and cooking. Currently working as Technical Director at mediaman in Shanghai.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade