The File Hierarchy Standard ([:FilesystemHierarchyStandard:FHS]) permits mounting parts of the filesystem readonly. This has some advantages like fewer filesystem checks on bootup or no need to check the whole filesystem after a crash.

Preconditions

The FHS allows mounting all below /bin, /lib, /sbin and /usr. But you can extend this much more by using different filesystems for some trees and take care for special files.

Locations they must be writable are /etc, /home, /tmp, /var. The hierarchies below /dev, /proc and /sys are already handled by special filesystems.

For /tmp you can use a tmpfs filesystem or its own filesystem. For /var it's prefered to use its own filesystem. An example can look like this:

Device file

Filesystem

Mount point

RO/RW ?

/dev/hda1

ext2

/

RO

/dev/hda2

ext3

/var

RW

tmpfs

/tmp

RW

/var/local/home

bind mount

/home

RW

You can use a filesystem without a journal for /, because you don't write there and you don't need the journal.

Special files in /etc

You have to take care for some files in /etc. These are

adjtime

because it's modified on boot up; see bug 156489

Solution: Create a symlink from /etc/adjtime to /var/local/adjtime and add the option --noadjtime to HWCLOCKPARS in /etc/init.d/hwclockfirst.sh and /etc/init.d/hwclock.sh or in lenny use the options --noadjfile --localtime / --utc

blkid.tab

because it's modified at runtime by libblkid1

Solution: Create a symlink from /etc/blkid.tab to /var/local/blkid.tab

mtab

used by mount

Solution: Create a symlink from /etc/mtab to /proc/mounts

nologin

modified on boot up by the initscripts bootmisc.sh and rmnologin

This should already be a symlink to /var/lib/initscripts/nologin

resolv.conf

If you have only a static nameserver configuration, then there's no problem. Otherwise you should use the package resolvconf.

passwd, shadow

These files might be modified by the user with the tools chfn, chsh and passwd. If you are the only user of you system, you can remount the filesystem read/write before using these tools. Otherwise you might think about using NIS or LDAP.

Enable readonly root

To make your root filesystem is mounted readonly you must edit your /etc/fstab and set the mount option ro.

# /etc/fstab: static file system information.
#
# <file system>     <mount point>   <type>  <options>               <dump>  <pass>
/dev/hda1           /               ext2    defaults,noatime,ro,errors=remount-ro       0 1
/dev/hda4           /var            ext3    defaults                0 2

The option noatime is useful while the disk is mounted read/write while updates.

Tips and tricks

Make apt-get remount / if needed

To make apt-get remounts the filesystem automatically read/write before calling dpkg and remounting it readonly after dpkg finished, put these lines in /etc/apt/apt.conf

DPkg {
    // Auto re-mounting of a readonly /usr
    Pre-Invoke { "mount -o remount,rw /"; };
    Post-Invoke { "check-etc; mount -o remount,ro / || true"; };
};

Find processes blocking the remount readonly

After an upgrade of packages you might be faced with the problem that mount refuses to remount the filesystem readonly telling you “/ is busy.” This is caused by deleted files they are still used by a process. To find out which processes use deleted files use the tool [http://manpages.debian.net/man/1/checkrestart checkrestart(1)] from the package debian-goodies or use the following command. Often these are daemons using upgraded libraries. You have to restart them to make the files are released.

% {lsof +L1; lsof|sed -n '/SYSV/d; /DEL\|(path /p;'} |grep -Ev '/(dev|home|tmp|var)'
COMMAND     PID     USER   FD   TYPE DEVICE    SIZE NLINK   NODE NAME
login      1546     root    4r   REG    3,3    1331     0  66165 /etc/passwd (deleted)
startx     1587    joerg   10r   REG    3,3    4491     0 295122 /usr/bin/startx
xinit      1609    joerg  txt    REG    3,3   19084     0 295565 /usr/bin/xinit
zsh-beta   5058    joerg  txt    REG    3,3  628968     0 458849 /bin/zsh-beta
zsh-beta   5058    joerg   12r   REG    3,3  174728     0 205450 /usr/share/zsh-beta/functions/Completion.zwc
zsh-beta   5058    joerg   13r   REG    3,3 2221256     0 205405 /usr/share/zsh-beta/functions/Completion/Unix.zwc
zsh-beta   5058    joerg   14r   REG    3,3  237528     0 205398 /usr/share/zsh-beta/functions/Completion/Base.zwc
udevd       458       root  mem       REG        3,3              131417 /lib/libnss_files-2.7.so (path inode=140638)
udevd       458       root  mem       REG        3,3              131431 /lib/libnss_nis-2.7.so (path inode=140653)
udevd       458       root  mem       REG        3,3              131389 /lib/libnsl-2.7.so (path inode=140616)
udevd       458       root  mem       REG        3,3              131401 /lib/libnss_compat-2.7.so (path inode=140623)
udevd       458       root  mem       REG        3,3              131212 /lib/libdl-2.7.so (path inode=140598)
udevd       458       root  mem       REG        3,3              131159 /lib/libc-2.7.so (path inode=140581)
udevd       458       root  mem       REG        3,3              131089 /lib/ld-2.7.so (path inode=140572)
syslog-ng  1406       root  mem       REG        3,3              131417 /lib/libnss_files-2.7.so (path inode=140638)
syslog-ng  1406       root  mem       REG        3,3              131431 /lib/libnss_nis-2.7.so (path inode=140653)
syslog-ng  1406       root  mem       REG        3,3              131389 /lib/libnsl-2.7.so (path inode=140616)
syslog-ng  1406       root  mem       REG        3,3              131401 /lib/libnss_compat-2.7.so (path inode=140623)
syslog-ng  1406       root  mem       REG        3,3              131159 /lib/libc-2.7.so (path inode=140581)
syslog-ng  1406       root  mem       REG        3,3              131089 /lib/ld-2.7.so (path inode=140572)