As some of you might own, I’m still rocking a ThinkPad T430, a laptop model originally released in 2012. It’s not the fastest laptop out there, but it is plenty fast for a number of tasks, even most software development work.

I also try to keep my machines up to date and properly backed up. On my laptop, this means having around a couple of scheduled tasks, such as restic backups. They don’t really take up a lot of time, but if an intensive scheduled task starts while you’re having a video call over Google Meet while also sharing the screen, things can get a bit slow, ruining the experience.

After a couple of these instances, I started thinking about finding a solution.

Scheduled tasks

Here’s a quick rundown of regular tasks that I like to run on my laptop.

Backups using restic: hourly backups to my home server. The file scanning process of the backup is quite intense, eating up a lot of my CPU time.

System updates using dnf and flatpak: usually run daily, because I’m too lazy to install the updates using a GUI, and at the same time I cannot afford to run out of date software.

Backups of critical files from my server to my laptop: there are some files I just cannot afford to lose. For this reason, I make a daily backup of the files to my laptop.

Filesystem maintenance: I don’t trust my storage devices (and neither should you), which is why I run a weekly scrub on my SSD that’s running the btrfs filesystem. If there are errors detected, I will know as soon as possible.

These tasks can be quite intensive, especially on hardware that’s a bit older.

With some tasks, such as system updates, you may also run into random issues. Updating Mozilla Firefox while it’s actively running results in the browser requiring a restart immediately after opening a new tab. This is very annoying and inconvenient when you’re in the middle of your work.

The “time-to-sleep” script

To avoid intensive scheduled tasks running while I’m doing actual work, I tried rescheduling these tasks to a more quiet time. All I needed to do was to edit the systemd timers so that the jobs start after midnight and leave the laptop running.

This solution has quite an obvious downside: my laptop will be powered on pretty much 24/7, and since electricity is super expensive and laptop maintenance a bit of a pain, I’d be wasting resources, time and eventually money.

I could make the last scheduled task make the machine shut down, but what if I have a long-running file copy operation running? It would be interrupted, unless I specifically disabled the scheduled task.

Then it clicked for me.

I know when I’m planning on going to sleep. Sleeping also has a regular cycle as it happens daily. Why not just put all the scheduled daily tasks into one script that I can run whenever I intend to shut down my laptop?

And thus, the time-to-sleep script was born. It’s a simple Bash script that lives in /root/.local/bin/time-to-sleep. To make sure I can also reasonably observe the behaviour of the script, I’ve implemented it as a simple systemd service that lives in /etc/systemd/system/time-to-sleep.service. It’s disabled by default, but can be started manually.

[Unit]
Description=Run maintenance tasks

[Service]
ExecStart=/root/.local/bin/time-to-sleep

I have an alias in ~/.bashrc that I use to start the service.

alias time-to-sleep="sudo systemctl start time-to-sleep.service"

Whenever the time comes to go to sleep, or when I’m just going to do something else and can leave the laptop running at my desk, I just open a terminal window, type time-to-sleep and I’m good to go. If I’m somewhere with a very poor internet connection or need to conserve battery power, I can opt to not run that script.

Going with this approach also saves me a lot of trouble on the technical side as well. I don’t need to think of a magical way to automatically detect if I’m working at the moment, or if I’m running on battery, or if I’m behind a limited internet connection. Sometimes your job becomes so much easier if you make some assumptions about when and where the script runs. You’re in control of choosing when to run it, after all.

In case something goes wrong, I can rely on the set -e line in my script to fail hard. When I come back to my machine, it will be quite noticeable that something has gone wrong, as the laptop will still be running. Who needs a fancy monitoring setup when you have visual evidence of failure?

As for the hourly backups, I solved that problem by only making backups of more important files. It takes much fewer resources compared to the full /home folder backup that runs as part of the time-to-sleep script.

Conclusion

After all this work, I don’t have to worry about any scheduled tasks hogging up all the resources while working. At the same time, I still get all the benefits that the scheduled tasks bring.

Example

In case you need it: here’s an example of the script that I’m running, with all the personal details scrubbed out. Feel free to use it as inspiration for your own time-to-sleep script!

#!/bin/bash

set -e

echo "Restic full home folder backup."
export RESTIC_REPOSITORY='sftp:backupuser@backupserver.lan:/path/to/repo/'
export RESTIC_PASSWORD='someverysecurepasswordgoeshere'

restic unlock

restic backup --verbose --exclude-caches --cleanup-cache \
  --iexclude=/home/*/downloads \
  --exclude=/home/*/.cache \
  --exclude=/home/*/.gradle \
  --exclude=/home/*/.local/share/Trash \
  /home

restic forget --prune \
  --keep-hourly 0 \
  --keep-daily 7 \
  --keep-monthly 0

echo "Restic full home folder backup done."

echo "Backing up important data from the server."

rsync -az --delete-before \
  user@myserver.lan:/path/to/nextcloud/ \
  /storage/backups/nextcloud/

chown -R localuser:localuser /storage/backups/nextcloud/

echo "Backing up important data from the server done."

echo "Updating system."

dnf update -y

flatpak update -y && flatpak uninstall --unused -y

echo "Updating system done."

echo "Scrubbing disk."
btrfs scrub start -B /
echo "Scrubbing disk done."

echo "All done! Going to sleep."

shutdown now