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.
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.
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
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
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
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.
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
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
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.
Beyond the basic daily timer, a few additional patterns are worth knowing for production backup setups.
Some setups want frequent snapshots of critical data alongside a daily full backup. Create two service/timer pairs with different ExecStart scripts and schedules.
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.
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.
Systemd makes it easy to confirm your backups ran without logging into every server. Key commands:
systemctl list-timers restic-backup.timer shows the last run time and next scheduled run at a glance.journalctl -u restic-backup.service -n 100 --no-pager shows the last 100 lines of backup output including errors.systemctl --failed lists all failed units including backup services that errored since last boot.systemctl start restic-backup.service runs the backup immediately outside the scheduled window - useful for testing after changes.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.
systemctl enable --now restic-backup.timer. Use Persistent=true to catch missed runs.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.Free tier. Private vault ready in seconds. Point your systemd timer at it.
No egress fees, cancel anytime, 7-day money-back guarantee