NeuroDynamic.Tech
Sign inJoin

Backups while you sleep: systemd timers and retention

A backup that runs itself every night: back up, then apply a keep-this-many rule. No cron syntax, no forgetting.

The founder · 9 min read ·

GOING FURTHER

Tested for real: I ran every command in this guide top-to-bottom on a fresh Ubuntu 24.04 virtual machine (4 CPUs, 8 GB RAM) on 13 August 2026. The run continued directly from the end state of the restic guide, and carried straight on into the off-site companion guide. Versions at test time: Ubuntu 24.04.4 LTS, restic 0.16.4, systemd 255.

Found a problem? Tell me via the contact page.

Placeholders: anything written in CAPS-WITH-DASHES, like YOUR-SERVER-IP, is a value you replace with your own. $USER is different — leave it exactly as written; the shell fills in your username for you, as explained below.

What you'll end up with

A backup that runs itself every night: back up, then apply a keep-this-many rule. No cron syntax, no forgetting.

The finished state from my test run:

$ systemctl list-timers restic-backup.timer
NEXT                        LEFT LAST PASSED UNIT                ACTIVATES
Fri 2026-08-14 02:30:00 UTC   9h -         - restic-backup.timer restic-backup.service

This guide has a companion: The off-site copy, and proving your backups still restore. It adds the copy on a second machine and the restore drill. Do this one first.

Who this is for

GOING FURTHER. You have done Backups that actually restore: restic from zero and kept its end state: a repository at /mnt/backup-drive/restic-repo, a password file at ~/.config/restic/password, and the ~/my-files folder. This guide starts exactly there.

Time and cost

The commands for this guide and its off-site companion took me about 5 minutes in total on the test machine, measured as one run. Budget 35 minutes for this half — there are two config files to understand, not just paste. Cost: nothing.

Words you'll meet

  • Unit — one systemd config file describing one job or service. Lives in /etc/systemd/system/. (basics)
  • Service unit — a unit that does something — here, running our backup commands. (basics)
  • Timer unit — a unit whose only job is firing a service on a schedule. Systemd's replacement for cron. (basics)
  • Journal — systemd's central log. journalctl reads it; everything our service prints lands there. (basics)
  • Retention policy — the rule for which old snapshots to keep and which to drop. (basics)

Before you start

  • The end state of the restic guide — repository, password file, ~/my-files.
  • Everywhere below, $USER fills in your username automatically as files are written — nothing to edit mid-line. Verify with the cat checks.

The steps

Step 1 — write the backup service

First the what: a service unit that runs two restic commands in order — back up, then apply retention. Type=oneshot means "run to completion and stop"; that is the right shape for a scheduled job. User= makes the backup run as you, so the timer and your terminal commands never fight over file ownership.

The block below writes the whole file in one go. The shell replaces every $USER with your username as it writes:

sudo tee /etc/systemd/system/restic-backup.service > /dev/null <<EOF
[Unit]
Description=restic backup of my-files (with retention and off-site copy)
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
User=$USER
ExecStart=/usr/bin/restic -r /mnt/backup-drive/restic-repo --password-file /home/$USER/.config/restic/password backup /home/$USER/my-files
ExecStart=/usr/bin/restic -r /mnt/backup-drive/restic-repo --password-file /home/$USER/.config/restic/password forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
EOF

(The description mentions an off-site copy because the companion guide adds one as a third command in this same file. Nothing off-site happens yet.)

About that second line — the retention policy. --keep-daily 7 --keep-weekly 4 --keep-monthly 6 keeps one snapshot per day for a week. It also keeps one per week for a month, and one per month for half a year. Everything older gets dropped, and --prune reclaims the space. Backups stay useful without eating the disk.

Check it worked. Print the file back:

cat /etc/systemd/system/restic-backup.service

You should see the file above with your actual username everywhere $USER was — for example User=tester. If you can still see a literal $USER, delete the file and repeat this step.

Step 2 — write the timer

Now the when. Two lines matter:

  • OnCalendar=*-*-* 02:30:00 means every day, any year-month-day, at 02:30. Night time, because backups deserve an idle machine.
  • Persistent=true is the line most guides forget. If the machine is off at 02:30, the job runs at next boot instead of silently skipping. Without it, a laptop that sleeps every night backs up never.
sudo tee /etc/systemd/system/restic-backup.timer > /dev/null <<EOF
[Unit]
Description=Nightly restic backup

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true

[Install]
WantedBy=timers.target
EOF

Check it worked. Print it back:

cat /etc/systemd/system/restic-backup.timer

You should see exactly the block above — no placeholders in this one.

Step 3 — switch the timer on

Tell systemd to re-read its files:

sudo systemctl daemon-reload

Enable the timer now and on every boot:

sudo systemctl enable --now restic-backup.timer

The important line is:

Created symlink /etc/systemd/system/timers.target.wants/restic-backup.timer → /etc/systemd/system/restic-backup.timer.

Check it worked. Ask systemd when it will fire:

systemctl list-timers restic-backup.timer

You should see something like:

NEXT                        LEFT LAST PASSED UNIT                ACTIVATES
Fri 2026-08-14 02:30:00 UTC   9h -         - restic-backup.timer restic-backup.service

1 timers listed.

The NEXT column shows a real date — tomorrow, 02:30. LAST and PASSED are - because it has never fired yet. That changes tonight.

Step 4 — fire it once by hand

Never let a scheduled job's first run happen unattended. Start the service directly — this is exactly what the timer will do at 02:30:

sudo systemctl start restic-backup.service

It returns silently when finished. Now read the service's status:

systemctl status restic-backup.service

Your output will vary, but look for:

○ restic-backup.service - restic backup of my-files (with retention and off-site copy)
     Loaded: loaded (/etc/systemd/system/restic-backup.service; static)
     Active: inactive (dead) since Thu 2026-08-13 16:36:00 UTC; 30ms ago
TriggeredBy: ● restic-backup.timer
    Process: 2725 ExecStart=/usr/bin/restic [...snipped...] (code=exited, status=0/SUCCESS)
    Process: 2735 ExecStart=/usr/bin/restic [...snipped...] (code=exited, status=0/SUCCESS)

This confused me the first time, so let me save you the moment: inactive (dead) is the success state for a oneshot service. It ran, it finished, it stopped. The lines that matter are the two status=0/SUCCESS — both restic commands exited cleanly.

The full output lives in the journal:

sudo journalctl -u restic-backup.service

A passing run includes:

Aug 13 16:35:59 ubuntu-testbed restic[2725]: using parent snapshot 95cb8fa0
Aug 13 16:35:59 ubuntu-testbed restic[2725]: snapshot ec6479d9 saved
Aug 13 16:36:00 ubuntu-testbed restic[2735]: Applying Policy: keep 7 daily, 4 weekly, 6 monthly snapshots
[...snipped...]
Aug 13 16:36:00 ubuntu-testbed systemd[1]: Finished restic-backup.service - restic backup of my-files (with retention and off-site copy).

Check it worked. You can find snapshot ... saved, Applying Policy, and the final Finished line.

That is the whole automation: back up, apply retention, every night at 02:30. Next, give the backup somewhere else to live — the off-site copy, and proving your backups still restore.

Something went wrong?

  • You see Active: inactive (dead) in systemctl status and assume failure → it means a oneshot service finished — this is success → look for status=0/SUCCESS on each Process: line instead.
  • You see a literal $USER inside a unit file when you cat it → it means the heredoc was quoted or copied oddly, so the shell never expanded it → delete the file and re-paste the whole block from the first line to EOF.

Undo all of this

Tested for real, in this order. It removes everything this guide added. (If you also did the off-site companion guide, run its undo first; deleting the service file below also removes the off-site line that guide appends.) The restic guide's undo then clears the rest.

Stop the timer and remove it from boot:

sudo systemctl disable --now restic-backup.timer

The important line is:

Removed "/etc/systemd/system/timers.target.wants/restic-backup.timer".

Delete both unit files:

sudo rm /etc/systemd/system/restic-backup.service /etc/systemd/system/restic-backup.timer

Tell systemd to forget them:

sudo systemctl daemon-reload

Check they are really gone. Ask for the timer:

systemctl list-timers restic-backup.timer

You should see something like:

0 timers listed.

And the service:

systemctl status restic-backup.service

The important line is:

Unit restic-backup.service could not be found.

0 timers listed and could not be found are the pass marks. To remove restic and the local repository as well, follow the Undo in the restic guide. I ran the undos back-to-back in that order and ended with a completely clean machine.

Where to go next


Last tested: 13 August 2026 on Ubuntu 24.04.4 LTS. Versions: restic 0.16.4 (Ubuntu archive), systemd 255. Tested as one continuous run with the off-site companion guide.


Tried it? Improved it?

Tell the forum what worked and what didn’t: real experience beats recommendations, and the best answers get folded back into this guide with credit.

Related guides