Skip to content

Instantly share code, notes, and snippets.

@bob-swinkels
Last active December 21, 2025 01:48
Show Gist options
  • Select an option

  • Save bob-swinkels/155bef7f572a2feb3036105726c7c221 to your computer and use it in GitHub Desktop.

Select an option

Save bob-swinkels/155bef7f572a2feb3036105726c7c221 to your computer and use it in GitHub Desktop.
Example for running scheduled Restic backups on MacOS

Replace bobswinkels with your username and place files at correct locations:

  • /Users/bobswinkels/bin/backup.sh
  • /Library/LaunchDaemons/com.restic-backup.plist

Launch deamon using:
sudo launchctl load /Library/LaunchDaemons/com.restic-backup.plist

Alternatively the script can also be run using a cron job:
0 */6 * * * /Users/bobswinkels/bin/backup.sh >> /var/log/restic-backup.log 2>&1

#!/bin/bash
# Prune backups and make a new backup
if [ "$EUID" -ne 0 ] # Check if running as root
then echo "Please run as root"
exit
fi
# Exit on failure, pipe failure
set -e -o pipefail
# Clean up lock if we are killed.
# If killed by systemd, like $(systemctl stop restic), then it kills the whole cgroup and all it's subprocesses.
# However if we kill this script ourselves, we need this trap that kills all subprocesses manually.
exit_hook() {
echo "In exit_hook(), being killed" >&2
jobs -p | xargs kill
restic unlock
}
trap exit_hook INT TERM SIGTERM
# Initialize all the option variables.
# This ensures we are not contaminated by variables from the environment.
verbose=0
SET_CLEAN=false
while :; do
case $1 in
--clean)
SET_CLEAN=true # Clean backups
;;
-v|--verbose)
verbose=$((verbose + 1)) # Each -v adds 1 to verbosity.
;;
-?*)
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
;;
*) # Default case: No more options, so break out of the loop.
break
esac
shift
done
# The syntax to backup using rlone is: rclone:<remote>:<path>
# export RESTIC_REPOSITORY=""
# export RESTIC_PASSWORD=""
# Set correct user home folder (when running as root)
# HOME="/Users/username"
# How many backups to keep.
RETENTION_HOURS=12
RETENTION_DAYS=14
RETENTION_WEEKS=6
RETENTION_MONTHS=12
RETENTION_YEARS=3
# Paths of the files and folders that need to get backupped
backup_paths=(
$HOME/.zsh_history
$HOME/Desktop
$HOME/Documents
$HOME/Movies
$HOME/Music
$HOME/Pictures
/etc/cups
)
# Files and folders that need to get ignored (wildcards only applicable to files)
ignore=(
# General Apple
*.DS_Store
*.DS_Store?
.AppleDouble
.LSOverride
# Thumbnails
._*
# Apple photo's library (backupped through icloud)
"Photos Library.photoslibrary"
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
Desktop.ini
desktop.ini
# Recycle Bin used on file shares
\$RECYCLE.BIN
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
)
if [ "$SET_CLEAN" = true ]; then
# Do cleaning
echo "##################################################################################"
echo "Starting cleaning: $(/bin/date)"
/usr/local/bin/restic forget --verbose=$verbose --prune --keep-hourly $RETENTION_HOURS --keep-daily $RETENTION_DAYS --keep-weekly $RETENTION_WEEKS --keep-monthly $RETENTION_MONTHS --keep-yearly $RETENTION_YEARS &
wait $!
echo "Backup & cleaning is done: $(/bin/date)"
echo "##################################################################################"
else
# Do Backup
echo "##################################################################################"
echo "Starting backup: $(/bin/date)"
/usr/local/bin/restic backup --verbose=$verbose --one-file-system --exclude-file=<(for f in "${ignore[@]}" ; do echo "$f" ; done) ${backup_paths[@]} &
wait $!
echo "Backup is done: $(/bin/date)"
echo "##################################################################################"
fi
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key>
<string>/Users/bobswinkels</string>
<key>PATH</key>
<string>/bin:/usr/bin:/usr/local/bin</string>
<key>RCLONE_CONFIG</key>
<string>/Users/bobswinkels/.config/rclone/rclone.conf</string>
</dict>
<key>Label</key>
<string>com.restic-backup.app</string>
<key>Program</key>
<string>/Users/bobswinkels/bin/backup.sh</string>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/var/log/restic-backup.log</string>
<key>StandardOutPath</key>
<string>/var/log/restic-backup.log</string>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>0</integer>
</dict>
</dict>
</plist>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment