Read-only root filesystem

In my previous posts about ramdisks on my Raspberry Pi here and here, I explained my goal to reduce write access to the sd card as far as possible.

The last step is to mount the sd card read-only, so its filesystems can almost never get damaged. This is the expected behaviour of most embedded devices. You can power-cycle them and do no damage to their internal storage.

First, you need to edit your /etc/fstab file to configure the root and boot filesystems to be mounted read-only. To do this, you change the option field (the 4th field) of the lines starting with /dev/mmcblk0p0 and /dev/mmcblk0p1 from defaults to ro:

With the next reboot, both your root and boot filesystems are write-protected and cannot be modified by the system. In my case, I needed to do some more changes, so that all system services run without errors.

If you log in to your system and want to change something, e.g. change a configuration file, then you can re-enable the write access with the following command:

and disable write access again with


My raspberry is executing the nginx webserver to process and deliver some php files. The php scripts are processed by the FastCGI module of nginx. This module sometimes tries to create temporary files in /var/lib/nginx/fastcgi. To disable this, edit the /etc/nginx/nginx.conf file and add this line to the http-section:

Then all temporary files get stored in the /tmp directory mounted as a ramdisk.


The raspberry does not have a realtime clock. During startup it gets the current time and date by querying a ntp server. To prevent the time jumping back to start of 1970 if the ntp server does not answer (e.g. due to network problems), the file /etc/ is written once per hour with the current time. During startup this time is restored to the system.

My raspberry is always connected to the network, so I can disable the fake-hwclock mechanism. Simply comment out all lines in /etc/cron.hourly/fake-hwclock by prepending a # character. Then reload the cron configuration with

Persistent ramdisk storage

As described here, I use a ramdisk for working data which is regularly (once per night) and during shutdown backed up to the sd card. For this to work, I added two lines to the init script managing the backup which enables write access to the sd card, then does the backup and then disables write access again:

This is a bit dangerous, because for a short time, the write access to the sd card is enabled and data is written. Only once per day and during shutdown. But it might happen that the power is switched off too early during shutdown and the filesystem could be damaged. Because of that I currently think about sending the ramdisk contents to another system over the network, so I never need to enable write access to the sd card.


Sudo tries to write a timestamp to the directory /var/lib/sudo everytime you use it. I have not yet found a solution to disable this, so sudo will print out an error message, but anyhow lets you execute your command under the root account.


Logrotate is a tool to prevent logfiles from growing indefinitely. By default, every week it compresses the logfiles to gziped files and discards compressed files older than 4 weeks. Logrotate writes internal info to a state file which is stored under /var/lib/logrotate. To move this file to a writeable location, edit the cron script /etc/cron.daily/logrotate and add the option –state /var/log/logrotate.state:


Every week an index of the man pages installed on the raspberry is built. This does not work on a read-only filesystem, so I disabled it by adding the line exit 0 to the beginning of the scripts /etc/cron.weekly/man.db and /etc/cron.daily/man.db. This is no important change, but my raspberry keeps sending me mails as long as the man-db scripts fails to update the index.

Package update check

Here I described how the raspberry could automatically check for raspbian updates. This also does not work on a read-only filesystem. As an intermediaty solution I added the mount / -o remount,rw and mount / -o remount,ro commands before and after the apt-get update call and run the script only once per week (sundays at 3:00 am) to reduce the risk of damaging the filesystem. This is no optimal solution, but I have no better one yet.

If you have any other or better ideas, feel free to leave a comment below!

11 thoughts on “Read-only root filesystem

  1. Pingback: Embedded Raspberry Pi – readonlyroot | The Infuriated Admin

  2. Hello Özen,

    I run into the same problem a few days ago. The read-only mounting did not work anymore and just tells me “mount: / is busy”. I think that this is caused by a process having opened a file on the file system for writing. To find these files, you can use the lsof command (good article on lsof from IBM: lsof lists all open file handles, which is a very long list. So you can filter out the files opened for writing by using e.g.

    lsof / | grep -E ‘[0-9]+w\s’

    lsof / lists all files opened on in a path starting with /. grep filters out lines containing numbers and a ‘w’.

    On my raspberry it is the dhcp client having opened the /var/lib/dhclient.wlan0.leases file for writing. The dhclient program has a command line option to set the path to this file, that would be useful to move it to a ramdisk location. But at the moment I have no idea who starts the dhclient process or where I can modity the command line.

    Hope that helps!



  3. I have been having this same issue and loved your solution. However, in my application I am using a fullscreen version of midori. Lightdm seems to fail when the root filesystem is set to ro. I don’t really get too many clues as to why. The following seems to be my only clue:

    [+2.79s] DEBUG: Activating VT 7
    [+2.81s] DEBUG: Registering session with bus path /org/freedesktop/DisplayManager/Session0
    [+2.83s] DEBUG: Session 1991 exited with return value 1
    [+2.83s] DEBUG: User session quit
    [+2.83s] DEBUG: Stopping display
    [+2.83s] DEBUG: Sending signal 15 to process 1913
    [+2.92s] DEBUG: Process 1913 exited with return value 0
    [+2.92s] DEBUG: X server stopped
    [+2.92s] DEBUG: Removing X server authority /var/run/lightdm/root/:0
    [+2.92s] DEBUG: Releasing VT 7
    [+2.92s] DEBUG: Display server stopped
    [+2.92s] DEBUG: Display stopped

    Any ideas? Thanks.

  4. I’m new to Linux, although not to embedded systems. I’m hoping to use a RPi for several future, and a current project, so your info here look great.

    However, when I made the changes to /etc/fstab you have above, I get a couple of “Fail”s in the bootup process. The first is
    mount: unknown filesystem type ‘tempfs’
    it does this two times, I assume for the two times I have it in fstab (I didn’t make a ‘home’ directory).

    There are a couple of others, but I’m assuming for the moment that’s because there is no tmp directory.

    Do I need to “apt-get install tempfs”, or are you not using Raspian wheezy, or is there something else I’m missing?

    However, my app can run headless, and seems to run fine, even on this, my first try, just to see if it would work, without any refinements. So far, so good,

  5. Pingback: Power Down Your Shairport Amplifier | TT's Blog

  6. to enable the use of sudo in on a read only rootfs, add in /etc/fstab:
    tmpfs /var/lib/sudo tmpfs nodev,nosuid,size=64k,mode=0700 0 0

  7. Hi,

    I tried this, but if / is mounted read-only, lightdm fails to start. There are various “session exited with return value 1″ messages in lightdm.log, but no obvious errors in Xorg.log. Any ideas?

  8. nevermind, I solved it. I had to mount /var/lib/lightdm as tmpfs, and set user-authority-in-system-dir=true in /etc/lightdm/lightdm.conf

  9. Pingback: Create a robust Raspberry PI setup for 24×7 operation | Narciso Cerezo

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">