Last active
December 16, 2022 02:41
-
-
Save mafredri/6864dd0a30dc4324b44131559f7adae2 to your computer and use it in GitHub Desktop.
Time Machine backup snapshotting on ZFS using zrepl for snapshot purging (for Samba server)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# /etc/cron.d/samba-timemachine-zfs-snapshot | |
# Create snapshots after completed Time Machine backups. | |
# Runs every two minutes to allow creating backups only after cleanup is | |
# completed. | |
*/2 * * * * root /usr/local/bin/timemachine-zfs-snapshot.sh rpool/share/timemachine |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env zsh | |
# /usr/local/bin/timemachine-zfs-snapshot.sh | |
setopt err_exit | |
PATH=/sbin:$PATH | |
DATASET=$1 | |
# Custom logic for detecting when a client completed a Time Machine backup, | |
# we're not relying on Samba user disconnect scripts because Time Machine | |
# often leaves the connection open indefinitely. | |
# | |
# Dataset structure is [dataset]/[user], inside each user dataset there is | |
# expected to be only one device backup, multiple devices under a user may | |
# create snapshots at a point where one machines backup is incomplete. | |
for dir in $(zfs get mountpoint -o value -H $DATASET)/*; do | |
user=${dir:t} | |
for bundle in $dir/*.backupbundle; do | |
# Files must exist, Results for instance is present only after first completed backup. | |
if [[ ! -f $bundle/com.apple.TimeMachine.Results.plist ]] || [[ ! -f $bundle/com.apple.TimeMachine.SnapshotHistory.plist ]]; then | |
continue | |
fi | |
# Avoid creating backup before cleanup has been performed. | |
if (( $(stat -c '%Z' $bundle/com.apple.TimeMachine.Results.plist) < $(stat -c '%Z' $bundle/com.apple.TimeMachine.SnapshotHistory.plist) )); then | |
continue | |
fi | |
latest=$(grep -A1 com.apple.backupd.SnapshotCompletionDate $bundle/com.apple.TimeMachine.SnapshotHistory.plist | tail -n1 | cut -d'>' -f2 | cut -d'<' -f1 | tr T _ | cut -d: -f1-3 | tr -d : | tr -d Z | tr -d -) | |
latest=tm_${latest}_000 # zrepl prefix and standard timestamp suffix. | |
if zfs list -t snap -H $DATASET/$user@$latest 1>/dev/null 2>&1; then | |
continue | |
fi | |
# Time Machine can keep modifying files for a while after completion. | |
if (( $(find $bundle \( -mmin -3 -o -cmin -3 \) -print -quit | wc -l) > 0 )); then | |
continue | |
fi | |
zfs snap $DATASET/$user@$latest | |
done | |
done | |
# Trigger zrepl snapshot pruning and replication. | |
zrepl signal wakeup timemachine |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# /etc/zrepl/zrepl.yml | |
jobs: | |
- name: timemachine | |
type: snap | |
filesystems: | |
"rpool/share/timemachine<": true | |
"rpool/share/timemachine": false | |
snapshotting: | |
type: manual | |
pruning: | |
keep: | |
- type: last_n | |
count: 15 | |
regex: "^tm_.*" | |
- type: grid | |
grid: 7x1d | 2x1w | |
regex: "^tm_.*" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment