Automate Restic Backups
with systemd timers

Systemd timers are the right way to schedule restic on Linux. They handle missed runs, log to journald, respect network dependencies, and don't fail silently like cron. This page gives you working copy-paste units.

Why systemd timers instead of cron

Cron jobs fail silently. If your 2am cron backup fails, you find out when you need to restore. Systemd timers log everything to journald, send failure notifications, handle missed runs when the machine was off, and wait for network availability before starting.

The two key advantages: Persistent=true means a missed run fires on next boot, and After=network-online.target means the backup waits until the network is actually up - not just started.

Step 1: Create the backup script

Save this as /usr/local/bin/restic-backup.sh and make it executable with chmod +x.

#!/bin/bash
set -euo pipefail
source /etc/restic/env

restic backup /home /etc /var/www \
  --tag "$(hostname)" \
  --exclude="/home/*/.cache" \
  --exclude="*/node_modules" \
  --exclude="*/.git"

restic forget \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 3 \
  --prune

restic check

Step 2: Store credentials securely

Never put credentials in the service unit - they show up in systemctl status. Use a root-only env file instead.

sudo mkdir -p /etc/restic
sudo install -m 600 /dev/null /etc/restic/env
sudo tee /etc/restic/env <<EOF
RESTIC_REPOSITORY=sftp:vaultuser@vault.servercrate.net:22150:/data
RESTIC_PASSWORD=your-repository-password
EOF

Step 3: Create the systemd service unit

Save as /etc/systemd/system/restic-backup.service:

[Unit]
Description=Restic offsite backup
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
EnvironmentFile=/etc/restic/env
ExecStart=/usr/local/bin/restic-backup.sh
Nice=10
IOSchedulingClass=idle
TimeoutStartSec=4h

[Install]
WantedBy=multi-user.target

Step 4: Create the timer unit

Save as /etc/systemd/system/restic-backup.timer:

[Unit]
Description=Run restic backup daily at 2:30 AM

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

[Install]
WantedBy=timers.target

Persistent=true fires the missed run on next boot. RandomizedDelaySec adds jitter so multiple servers don't all hit the backup target simultaneously.

Step 5: Enable and verify

sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer

# Check timer is active
systemctl list-timers restic-backup.timer

# Check logs after first run
journalctl -u restic-backup.service -n 50 --no-pager

# Run manually to test
sudo systemctl start restic-backup.service

Adding failure notifications

Add OnFailure=notify-email@%n.service to the [Unit] section if you have a mail setup, or use a simple curl to a webhook or Healthchecks.io at the end of your backup script.

# Add to end of restic-backup.sh
curl -fsS --retry 3 https://hc-ping.com/your-uuid > /dev/null 2>&1 || true

Where to point your restic repository

The SFTP backend is the cleanest option for private offsite backup. ServerCrate provides a dedicated private vault with a ZFS-backed SFTP endpoint, flat monthly pricing, and no egress fees. Point your RESTIC_REPOSITORY at your vault connection string from the portal and the rest of this setup works as-is.

Common systemd timer patterns for restic

Beyond the basic daily timer, a few additional patterns are worth knowing for production backup setups.

Multiple schedules (hourly + daily)

Some setups want frequent snapshots of critical data alongside a daily full backup. Create two service/timer pairs with different ExecStart scripts and schedules.

Network dependency

After=network-online.target in the service unit prevents the backup from running before the interface is up. Essential for SFTP-based remotes. Also add Wants=network-online.target to ensure the unit waits for it.

OnCalendar syntax

Some useful OnCalendar values: daily (same as *-*-* 00:00:00), weekly, Mon *-*-* 02:00:00 for Monday at 2am, *-*-* 02,14:30:00 for twice daily. Run systemd-analyze calendar "your expression" to validate.

Monitoring systemd backup health

Systemd makes it easy to confirm your backups ran without logging into every server. Key commands:

  • Check timer status:systemctl list-timers restic-backup.timer shows the last run time and next scheduled run at a glance.
  • Read logs:journalctl -u restic-backup.service -n 100 --no-pager shows the last 100 lines of backup output including errors.
  • Check for failures:systemctl --failed lists all failed units including backup services that errored since last boot.
  • Test manually:systemctl start restic-backup.service runs the backup immediately outside the scheduled window - useful for testing after changes.

Why ServerCrate works well with systemd timers

An SFTP-based backup target like ServerCrate is the cleanest match for unattended systemd timer jobs. The SFTP connection authenticates via SSH keys stored in /root/.ssh/, so no interactive password prompt ever blocks an automated run. The private vault persists between runs, and the portal gives you a dashboard to verify snapshot dates without logging into the backup server directly. See the hosted Restic backup server page for more on why SFTP is the right backend for automated Linux backups.

FAQ

Common questions.

Create a .service unit that calls your backup script and a .timer unit with OnCalendar set to your schedule. Enable the timer with systemctl enable --now restic-backup.timer. Use Persistent=true to catch missed runs.
Yes. Systemd timers handle missed runs, log to journald, support network dependencies, and report failures properly. Cron fails silently and has no native dependency system.
Store it in /etc/restic/env with chmod 600 and source the file in your backup script. Use EnvironmentFile in the service unit. Never put credentials directly in the unit file.
Run journalctl -u restic-backup.service -n 50 to see recent logs. Run systemctl list-timers restic-backup.timer to see the next scheduled run and when it last ran.
Get started today

Automate your backups today.

Free tier. Private vault ready in seconds. Point your systemd timer at it.

No egress fees, cancel anytime, 7-day money-back guarantee