Minimizing disk activity on linux

One common task I have had recently is to minimize the disk usage on a Ubuntu linux install. The reason why I want to do this is to prolong the life of the storage device. In my case Ubuntu is installed on a microSD card.

All of this applies to almost any modern linux distribution.

Filesystem mount options

By default, the ext4 filesystem records the access time of files on the filesystem. I see no reason to record this information. It's common to disable it in most circumstances.

You can edit /etc/fstab as root in order to disable this feature. The file has 1 line per filesystem mounted. The line you are looking for looks like this

UUID=b22312e6-a495-4a5b-959f-4d73ec97e9bf /               ext4    errors=remount-ro 0       1

The first item on the line identifies the filesystem, the second the mount point, the third the filesystem type, and the fourth is the options.

You can safely change the fourth item to update the mount options to look more like this. The UUID is unique for the filesystem you are running on.

UUID=b22312e6-a495-4a5b-959f-4d73ec97e9bf /               ext4    discard,noatime,nodiratime,errors=remount-ro 0       1

I added the following

  1. discard - tell the underlying device to discard blocks. This has no effect if the device does not support it
  2. noatime - do not record the access time of file
  3. nodiratime - do not record the directory access time

After you've made the change, you can apply the change immediately by running sudo mount -o remount / in a shell. This remounts the filesystem and lets you know that you have the options set correctly in /etc/fstab.

Reconfigure journalD

journalD is the component of systemd that is responsible for recording all the system logs. Logs are normally stored to disk. If you don't need your logs persisted you can save wear and tear on your storage device.

Volatile storage

To use only volatile storage, you need to edit /etc/systemd/journald.conf and find the [Journal] section. It is usually the only section in the configuration file.

The first value is to set Storage=volatile. This tells systemd that all the storage to be used is in memory.

The next value is RuntimeMaxUse=128m. This tells systemd how much memory to use to store logs. You can set it to whatever you like, depending on how much system memory you have.

Disable forwarding to syslog

journalD normally forwards all the logs to the syslog file as well. This obviously uses the storage device. So you can set ForwardToSyslog=no in the /etc/systemd/journald.conf to disable this behavior.

Some logs are still written to syslog, but the normal system service logs are no longer written to syslog in this case.

journald.conf example

This is what my entire /etc/systemd/journald.conf file looks like now

[Journal]
Storage=volatile
RuntimeMaxUse=128M
ForwardToSyslog=no

Restart journalD

To apply the above changes, run the command sudo systemctl restart systemd-journald in a shell.

Clean out old logs

Since systemd was running with persistent storage in the past, it still has those logs in file somewhere. You can tell it to get rid of all the old logs by running sudo journalctl --vacuum-time=1m. This deletes any logs older than 1 minute.

Set swappiness to 1

If your linux installation has swap configured (most do) then idle processes eventually have memory pages stored on disk instead of system memory. This is desirable because it means that long lived idle processes aren't using system memory. How quickly this happens is controlled by a value called swappiness. You can check this by running cat /proc/sys/vm/swappiness. This should show you a number 0-100. It's usually 60 by default.

This value can be set to a lower number by editing /etc/sysctl.conf as root. Usually there is no line for this value so you can just add a line like this

vm.swappiness = 1

A value of 1 allows processes to still be moved to swap. A value of 0 means processes will only be used to swap when completely out of memory. You can apply this change immediately by running sudo sysctl -p.

Disable the IO scheduler

Any block device on linux has an associated IO scheduler. This works well enough for conventional hard drives. For a microSD card, the performance is usually pretty poor anyways. The IO scheduler does not necessarily improve performance. You can disable it often with no real loss of usability. This also uses slightly less CPU.

To disable the IO scheduler, you need to select the none scheduler for the root filesystem device. You can run findmnt and get an output like this

$ findmnt
TARGET                                SOURCE     FSTYPE     OPTIONS
/                                     /dev/mmcblk0p1
│                                                ext4       rw,noatime,nodiratime,errors=remount-ro,commit=600
├─/sys                                sysfs      sysfs      rw,nosuid,nodev,noexec,relatime
│ ├─/sys/kernel/security              securityfs securityfs rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/cgroup                    tmpfs      tmpfs      ro,nosuid,nodev,noexec,mode=755
│ │ ├─/sys/fs/cgroup/unified          cgroup2    cgroup2    rw,nosuid,nodev,noexec,relatime,nsdelegate
│ │ ├─/sys/fs/cgroup/systemd          cgroup     cgroup     rw,nosuid,nodev,noexec,relatime,xattr,name=systemd
│ │ ├─/sys/fs/cgroup/rdma             cgroup     cgroup     rw,nosuid,nodev,noexec,relatime,rdma
│ │ ├─/sys/fs/cgroup/memory           cgroup     cgroup     rw,nosuid,nodev,noexec,relatime,memory
│ │ ├─/sys/fs/cgroup/net_cls,net_prio cgroup     cgroup     rw,nosuid,nodev,noexec,relatime,net_cls,net_prio
│ │ ├─/sys/fs/cgroup/devices          cgroup     cgroup     rw,nosuid,nodev,noexec,relatime,devices
│ │ ├─/sys/fs/cgroup/cpuset           cgroup     cgroup     rw,nosuid,nodev,noexec,relatime,cpuset
│ │ ├─/sys/fs/cgroup/blkio            cgroup     cgroup     rw,nosuid,nodev,noexec,relatime,blkio
│ │ ├─/sys/fs/cgroup/cpu,cpuacct      cgroup     cgroup     rw,nosuid,nodev,noexec,relatime,cpu,cpuacct
│ │ ├─/sys/fs/cgroup/perf_event       cgroup     cgroup     rw,nosuid,nodev,noexec,relatime,perf_event
│ │ ├─/sys/fs/cgroup/pids             cgroup     cgroup     rw,nosuid,nodev,noexec,relatime,pids
│ │ ├─/sys/fs/cgroup/hugetlb          cgroup     cgroup     rw,nosuid,nodev,noexec,relatime,hugetlb
│ │ └─/sys/fs/cgroup/freezer          cgroup     cgroup     rw,nosuid,nodev,noexec,relatime,freezer
│ ├─/sys/fs/pstore                    pstore     pstore     rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/bpf                       none       bpf        rw,nosuid,nodev,noexec,relatime,mode=700
│ ├─/sys/kernel/debug                 debugfs    debugfs    rw,nosuid,nodev,noexec,relatime
│ │ └─/sys/kernel/debug/tracing       tracefs    tracefs    rw,nosuid,nodev,noexec,relatime
│ ├─/sys/kernel/tracing               tracefs    tracefs    rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/fuse/connections          fusectl    fusectl    rw,nosuid,nodev,noexec,relatime
│ └─/sys/kernel/config                configfs   configfs   rw,nosuid,nodev,noexec,relatime
├─/proc                               proc       proc       rw,nosuid,nodev,noexec,relatime
│ └─/proc/sys/fs/binfmt_misc          systemd-1  autofs     rw,relatime,fd=29,pgrp=1,timeout=0,minproto=5,maxpro
├─/dev                                udev       devtmpfs   rw,nosuid,noexec,relatime,size=425668k,nr_inodes=106
│ ├─/dev/pts                          devpts     devpts     rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=00
│ ├─/dev/shm                          tmpfs      tmpfs      rw,nosuid,nodev
│ ├─/dev/hugepages                    hugetlbfs  hugetlbfs  rw,relatime,pagesize=2M
│ └─/dev/mqueue                       mqueue     mqueue     rw,nosuid,nodev,noexec,relatime
├─/run                                tmpfs      tmpfs      rw,nosuid,nodev,noexec,relatime,size=100156k,mode=75
│ ├─/run/lock                         tmpfs      tmpfs      rw,nosuid,nodev,noexec,relatime,size=5120k
│ └─/run/user/1000                    tmpfs      tmpfs      rw,nosuid,nodev,relatime,size=100152k,mode=700,uid=1
├─/tmp                                tmpfs      tmpfs      rw,nosuid,relatime
├─/var/log.hdd                        /dev/mmcblk0p1[/var/log]
│                                                ext4       rw,noatime,nodiratime,errors=remount-ro,commit=600
└─/var/log                            /dev/zram1 ext4       rw,relatime,discard

The only line we care about is the first one listed as /. The source in my example is /dev/mmcblk0p1. The name of the device is mmcblk0p1. The current scheduler can be checked by running

$ cat /sys/block/mmcblk0/queue/scheduler 
none mq-deadline kyber [bfq]

The entry with the brackets around it is the current scheduler. If the brackets are around [none] then the IO scheduler is already disabled. If it is not set, then a udev rule can be added to do so automatically.

Create the file /etc/udev/rules.d/99-scheduler.rules. Only one line needs to be added

ACTION=="add|change", SUBSYSTEM=="block", KERNEL=="mmcblk0p1", ATTR{queue/scheduler}="none"

The value KERNEL=="mmcblk0p1" identifies the device by its name, so you need to update it with your devices name. You can also use wildcards, such as KERNEL=="mmc*" to match all devices with a name starting with mmc.

To apply this change, run sudo udevadm control --reload-rules followed by sudo udevadm trigger --type=devices --action=change. Then check the current scheduler again

$ cat /sys/block/vda/queue/scheduler 
[none] mq-deadline kyber bfq

Copyright Eric Urban 2022, or the respective entity where indicated