Moving fake-hwclock to a separate partition on a read-only Raspberry Pi
Part of the Raspberry Pi Reliability series.
Both iterations of my guide to running a Raspberry Pi with a read-only root filesystem have one hacky caveat: they still allow fake-hwclock
to write to the filesystem every hour. I didn’t love this approach:
- It’s possible, if unlikely, for some other task to start writing to the filesystem at the same time and then prevent remounting the root filesystem as read-only.
- It’s just not an ideologically sound solution.
So I decided, on one of my read-only Pis, to move fake-hwclock
’s data to an external USB drive. This is an approach I’ve used a few times now:
- On my system running BirdNET-Pi, I’ve moved bird song recordings & processing to an external drive to reduce wear on the Pi’s SD card1.
- On another Pi that, for silly legacy reasons, needs some swap space, I’m using a sacrificial external drive as swap.
Moving forward, I will try to remember to set up a dedicated partition on the SD card for fake-hwclock
when I first set up a new Pi. But I can’t repartition this SD card and downsize an existing partition in place, so a cheap small USB drive will suffice.
The information in this post is, to the best of my knowledge, current as of June 2024. It should work on Raspberry Pi OS versions 11 (Bullseye) and 12 (Bookworm), at least, but I make no promises.
Though this approach carries relatively little risk compared to the overall risks you took to make your Pi read-only, remember that there's always some risk. In particular, double-check your partitioning and mkfs
commands before committing to them! (See my Pi Reliability post on risk vs. benefits.)
Part 1: Set up the USB drive
Connect the USB drive to your Pi. Confirm its name via lsblk
. In this case, my drive is named sda
and it’s already partitioned, so I’ll be using sda1
in the following steps:
$ lsblk
sda 8:0 1 29.3G 0 disk
└─sda1 8:1 1 29.3G 0 part
mmcblk0 179:0 0 59.7G 0 disk
├─mmcblk0p1 179:1 0 256M 0 part /boot
└─mmcblk0p2 179:2 0 59.4G 0 part /
Format the drive’s single partition with ext4 via sudo mkfs.ext4 /dev/sda1
. This will print the UUID of the partition, or you can get it via lsblk -f
:
$ lsblk -f
sda
└─sda1 ext4 1.0 cda25d93-8705-4824-847f-d7c8c34b5289
mmcblk0
├─mmcblk0p1 vfat FAT32 bootfs 9E81-4F92 203.5M 20% /boot
└─mmcblk0p2 ext4 1.0 rootfs cf2895ca-6dc2-4797-8040-f76ba1508f41 51.4G 8% /
Remount the root partition as read-write. If you added the shell integration I recommended, this is as simple as running the command rw
.
Create a mount point for the drive. I’m calling mine /mnt/mut
, since this is for storing mutable data: sudo mkdir /mnt/mut
Add the drive to /etc/fstab
, using the UUID from earlier. I added this line to my fstab
:
UUID=cda25d93-8705-4824-847f-d7c8c34b5289 /mnt/mut ext4 defaults,noatime 0 1
Mount the drive via sudo mount -a
. You shouldn’t get any errors at this point; if you do, you likely got something wrong in /etc/fstab
.
I made my drive world-writable and world-readable, for ease of use. This is a single-user Pi, so locking down this drive isn’t a concern for me: sudo chmod 0777 /mnt/mut
Finally, change into /mnt/mut
and verify that you can write a file and read it back:
$ cd /mnt/mut
/mnt/mut$ echo "hello world" > ./test.txt
/mnt/mut$ cat ./test.txt
hello world
/mnt/mut$ rm ./test.txt
Part 2: Reconfigure fake-hwclock
Configure fake-hwclock
to use a new data storage location via sudo nano /etc/default/fake-hwclock
. Add the following line:
FILE=/mnt/mut/fake-hwclock.data
We need to make some changes to fake-hwclock
’s cron job to make it respect this setting. Edit it via sudo nano /etc/cron.hourly/fake-hwclock
. The file should look like this:
#!/bin/sh
#
# Simple cron script - save the current clock periodically
# https://www.dzombak.com/blog/2024/06/Moving-fake-hwclock-to-a-separate-partition-on-a-read-only-Raspberry-Pi.html
#
if (command -v fake-hwclock >/dev/null 2>&1) ; then
PARAM=/etc/default/fake-hwclock
if [ -f "$PARAM" ]; then
set -o allexport
. "$PARAM"
set +o allexport
fi
fake-hwclock save
fi
We also need to tell systemd
that fake-hwclock
now depends on an additional mount point. Edit the systemd
unit file via sudo systemctl edit fake-hwclock.service
, and add this content:
[Unit]
RequiresMountsFor=/mnt/mut/fake-hwclock.data
Finally, we’ll remove the old fake-hwclock
data to avoid any confusion and cause a clear failure if something tries to access it: sudo rm /etc/fake-hwclock.data
Time to test it! Remount the root partition as read-only. With my shell integration, this is as simple as running the command ro
.
First we’ll confirm that fake-hwclock is using the new storage location. Tell systemd
to reload the daemon and restart it, then check its logs to make sure it didn’t hit an error trying to write to the (read-only) root partition:
$ sudo systemctl daemon-reload
$ sudo systemctl restart fake-hwclock.service
$ sudo journalctl -u fake-hwclock.service
-- Journal begins at Tue 2024-06-25 17:17:01 EDT. --
Jun 25 17:17:01 workshop-pi fake-hwclock[149]: Tue 25 Jun 2024 09:17:01 PM UTC
Jun 26 09:40:43 workshop-pi systemd[1]: Stopping Restore / save the current clock...
Jun 26 09:40:43 workshop-pi systemd[1]: fake-hwclock.service: Succeeded.
Jun 26 09:40:43 workshop-pi systemd[1]: Stopped Restore / save the current clock.
Jun 26 09:40:43 workshop-pi systemd[1]: Starting Restore / save the current clock...
Jun 26 09:40:43 workshop-pi fake-hwclock[19944]: Wed 26 Jun 2024 01:40:43 PM UTC
Jun 26 09:40:43 workshop-pi systemd[1]: Finished Restore / save the current clock.
$ cat /mnt/mut/fake-hwclock.data
2024-06-26 13:40:43
Similarly, confirm that the cron job runs without an error and updates /mnt/mut/fake-hwclock.data
with the current time:
$ sudo /etc/cron.hourly/fake-hwclock
$ cat /mnt/mut/fake-hwclock.data
2024-06-26 13:56:13
Finally, time to reboot: sudo reboot now
Once the system comes back up, check the logs for fake-hwclock
to see if it encountered any errors: sudo journalctl -u fake-hwclock.service
And you’re done.
References
- My most recent read-only Rabperry Pi guide (and the previous iteration)
fake-hwclock
manpage- Raspberry Pi forums: fake-hwclock with read only filesystem
- Stack Overflow: Set environment variables from file of key/value pairs
My fork of the software is at github.com/cdzombak/BirdPi. Right now it mainly contains in-progress work to move all recording and processing to ffmpeg, allowing for e.g. white noise filters. I’m also working on moving temporary recordings to a
tmpfs
in memory for performance and further flash drive wear reduction. I’ll post about that once it’s done, someday. ↩