cheat sheet

tmutil

Drive Time Machine backups, destinations, exclusions, local APFS snapshots, restores, and verification entirely from the terminal — without opening System Settings.

tmutil — Time Machine from the Command Line

What it is

tmutil is the command-line interface to Time Machine, macOS's built-in incremental backup system. It manages destinations, triggers on-demand backups, lists and prunes the local APFS snapshots that Time Machine creates between off-machine backups, restores files, and inspects the integrity of the backup database. Reach for tmutil whenever Time Machine needs to be scripted — running an unattended backup before a system update, pruning local snapshots when the internal disk is full, restoring a file in a CI pipeline, or auditing how many backup destinations a fleet of Macs has configured. Most write operations require sudo and Full Disk Access for the controlling terminal.

The two key concepts are destinations (NAS volumes, external disks, or Time Capsule shares that hold the off-machine archive) and local snapshots (APFS snapshots written to the source disk every hour, kept for ~24 hours, used to bridge the gap when the destination is unreachable). tmutil exposes both.

Install

tmutil lives in /usr/bin/tmutil and ships with every macOS install. Confirm it is present.

bash
which tmutil
tmutil version

Output:

text
/usr/bin/tmutil
Time Machine command line utility (built Apr  3 2026)

Some sub-commands (notably restore, addexclusion, inheritbackup) require Full Disk Access for the calling terminal — grant via System Settings → Privacy & Security → Full Disk Access.

Syntax

tmutil takes a verb and arguments. Verbs split into four categories: state queries, destination management, backup actions, and snapshot/exclusion tools.

bash
tmutil VERB [ARGS...]

Output: (none — exits 0 on success)

CategoryVerbs
Statestatus, version, latestbackup, currentphase, machinedirectory
Destinationsdestinationinfo, setdestination, removedestination
Service controlenable, disable, startbackup, stopbackup
Snapshots & exclusionslocalsnapshot, listlocalsnapshots, deletelocalsnapshots, thinlocalsnapshots, addexclusion, removeexclusion, isexcluded
Backup inspectionlistbackups, listsizes, compare, verifychecksums
Restorerestore
Plumbingassociatedisk, inheritbackup, uniquesize

Essential verbs (one-line summary)

VerbPurpose
tmutil statusOne-shot snapshot of current backup activity
tmutil destinationinfoList configured destinations
tmutil latestbackupPath to the most recent successful backup
tmutil listbackupsPath to every backup, oldest first
tmutil startbackupBegin a backup right now
tmutil stopbackupCancel a backup in progress
tmutil enable / disableTurn the schedule on/off
tmutil localsnapshotCreate one APFS local snapshot immediately
tmutil listlocalsnapshots /List local snapshots on a mountpoint
tmutil deletelocalsnapshots DATEDelete one local snapshot
tmutil thinlocalsnapshots /Free space by deleting oldest snapshots
tmutil addexclusion PATHStop backing up a path
tmutil restore SRC DESTRestore a file from a backup
tmutil compareDiff a backup against the live filesystem
tmutil setdestinationConfigure a backup target

Current backup state (status)

tmutil status returns a plist-style dictionary describing exactly what Time Machine is doing this instant — including the progress percentage, current phase, and the destination identifier.

bash
tmutil status

Output:

text
Backup session status:
{
    BackupPhase = Copying;
    ClientID = "com.apple.backupd";
    DateOfStateChange = "2026-05-25 09:42:11 +0000";
    DestinationID = "8B2F3A1C-4D5E-6F70-A1B2-C3D4E5F6A7B8";
    DestinationMountPoint = "/Volumes/Backup";
    FractionDone = "0.4231";
    Percent = "0.4231";
    Progress =     {
        "_raw_totalBytes" = 184321540608;
        bytes = 78012345600;
        files = 12483;
        totalFiles = 29871;
    };
    Running = 1;
    Stopping = 0;
}

The Running and BackupPhase keys are the two you want in a script.

bash
# Just "is a backup running right now?"
tmutil status | awk -F' = ' '/Running/ {gsub(";",""); print $2}'

Output:

text
1
bash
# Pretty percentage
tmutil status | awk -F'"' '/Percent/ {printf "%.0f%%\n", $2*100}'

Output:

text
42%

Destinations (destinationinfo, setdestination)

A destination is the off-machine archive — an external disk, a network share, or a Time Capsule. tmutil destinationinfo shows the live configuration and per-destination state including the date of the last completed backup.

bash
tmutil destinationinfo

Output:

text
====================================================
Name          : Backup
Kind          : Local
Mount Point   : /Volumes/Backup
ID            : 8B2F3A1C-4D5E-6F70-A1B2-C3D4E5F6A7B8
====================================================
Name          : NAS
Kind          : Network
URL           : smb://timemachine@nas.local/TM
ID            : C4F6E2D8-1A2B-3C4D-5E6F-708192A3B4C5
====================================================

Adding a destination

A local disk destination is set by mount point. Network destinations want a full URL.

bash
# Local disk (must be mounted; APFS-formatted; "Backups Off" allowed)
sudo tmutil setdestination /Volumes/Backup

# Network share (Time Machine over SMB)
sudo tmutil setdestination -a "smb://timemachine:secret@nas.local/TM"

Output: (none — exits 0)

The -a flag adds a destination (multiple destinations are supported and rotated through), without it the previous destination(s) are replaced.

Removing a destination

bash
sudo tmutil removedestination 8B2F3A1C-4D5E-6F70-A1B2-C3D4E5F6A7B8

Output: (none — exits 0)

Service control (enable, disable, startbackup, stopbackup)

enable / disable toggle the automatic schedule. startbackup / stopbackup start or cancel a single run regardless of schedule.

bash
sudo tmutil enable                       # automatic backups on
sudo tmutil disable                      # automatic backups off

tmutil startbackup                       # synchronous (blocks)
tmutil startbackup --block               # explicit synchronous
tmutil startbackup --auto                # respect schedule rules (rotation etc.)
tmutil startbackup --destination 8B2F…   # use a specific destination
tmutil startbackup --rotation            # cycle through destinations
tmutil stopbackup                        # cancel current backup

Output: (none — exits 0)

--block is the right choice in a shell script that wants to wait for the backup to complete and report exit status. Without --block the call returns immediately and the backup runs in the background.

Querying recent backups (latestbackup, listbackups)

latestbackup returns the filesystem path to the most recent successful backup. On APFS-formatted destinations this is a snapshot path that mounts read-only.

bash
tmutil latestbackup

Output:

text
/Volumes/.timemachine/8B2F3A1C-…/2026-05-25-094211.backup/2026-05-25-094211.backup
bash
tmutil listbackups

Output:

text
/Volumes/.timemachine/…/2026-05-23-211201.backup/2026-05-23-211201.backup
/Volumes/.timemachine/…/2026-05-24-001003.backup/2026-05-24-001003.backup
…
/Volumes/.timemachine/…/2026-05-25-094211.backup/2026-05-25-094211.backup

To find when the most recent backup ran:

bash
tmutil latestbackup | grep -Eo "\d{4}-\d{2}-\d{2}-\d{6}" | head -1

Output:

text
2026-05-25-094211

Local snapshots — the day-to-day safety net

Local snapshots are APFS snapshots of the boot volume, created hourly by backupd and kept for ~24 hours. They live on the source disk, not the destination, and provide the "go back an hour" behaviour even when the external drive is unplugged. Disk pressure or running tmutil thinlocalsnapshots / removes the oldest ones.

bash
tmutil listlocalsnapshots /

Output:

text
com.apple.TimeMachine.2026-05-25-080000.local
com.apple.TimeMachine.2026-05-25-090000.local
com.apple.TimeMachine.2026-05-25-100000.local
com.apple.TimeMachine.2026-05-25-110000.local
bash
# List for every mounted APFS volume in one go
for mp in / /System/Volumes/Data /Volumes/External; do
    [ -d "$mp" ] && printf '%s\n' "$mp" "$(tmutil listlocalsnapshots "$mp" | sed 's/^/  /')"
done

Output:

text
/
  com.apple.TimeMachine.2026-05-25-080000.local
  com.apple.TimeMachine.2026-05-25-090000.local
/System/Volumes/Data
  com.apple.TimeMachine.2026-05-25-080000.local
  com.apple.TimeMachine.2026-05-25-090000.local

Creating a local snapshot on demand

bash
tmutil localsnapshot

Output:

text
Created local snapshot with date: 2026-05-25-094300

This is the safest "save point" before a risky operation: it costs ~no disk space at creation time and gives a complete, file-level rollback for ~24 hours.

Deleting one local snapshot

The argument is the snapshot date (yyyy-mm-dd-HHMMSS), not the full label.

bash
sudo tmutil deletelocalsnapshots 2026-05-25-080000

Output:

text
Deleted local snapshot '2026-05-25-080000'

Bulk-pruning to free space (thinlocalsnapshots)

thinlocalsnapshots PATH MINPURGEABLEBYTES MAXAGE deletes the oldest snapshots until either the requested number of purgeable bytes is reclaimed or no snapshots older than MAXAGE seconds remain. This is the right way to reclaim space — not manually deleting snapshots one by one.

bash
# Free 20 GB by deleting oldest snapshots
sudo tmutil thinlocalsnapshots / 21474836480 1

Output:

text
Thinned local snapshots:
2026-05-24-100000
2026-05-24-110000
2026-05-24-120000

The third argument (1 above) is the urgency / purgeableSpaceTarget enum: 1 = highest urgency (delete eagerly), 4 = lowest. Apple's own daemons use 4; 1 is the value to use when freeing space proactively.

Exclusions (addexclusion, removeexclusion, isexcluded)

By default Time Machine backs up everything on the boot volume except items in the system's exclude list (caches, /private/var/db/com.apple.backupd.user, etc.). addexclusion lets you mark a path so neither full backups nor local snapshots include it.

bash
# Exclude a node_modules tree (it's regenerable)
sudo tmutil addexclusion /Users/alice/Code/big-project/node_modules

# Check whether a path is excluded
tmutil isexcluded /Users/alice/Code/big-project/node_modules

Output:

text
[Excluded]    /Users/alice/Code/big-project/node_modules
bash
# Remove the exclusion
sudo tmutil removeexclusion /Users/alice/Code/big-project/node_modules
tmutil isexcluded /Users/alice/Code/big-project/node_modules

Output:

text
[Included]    /Users/alice/Code/big-project/node_modules

There are two flavours of exclusion: sticky (default, stored in an extended attribute on the file itself) and path-based (in /Library/Preferences/com.apple.TimeMachine.plist). The default addexclusion writes a sticky exclusion; use -p for path-based when you want the exclusion to follow the path regardless of which file is at that path.

bash
sudo tmutil addexclusion -p /Users/alice/Downloads

Output:

text
[Excluded]    /Users/alice/Downloads

Inspect the path-based exclusion list directly.

bash
defaults read /Library/Preferences/com.apple.TimeMachine SkipPaths

Output:

text
(
    "/Users/alice/Downloads",
    "/private/var/folders"
)

Restoring (restore)

tmutil restore SOURCE DEST copies a path out of any backup snapshot back to a chosen location. The source path must include the backup snapshot prefix returned by tmutil listbackups or tmutil latestbackup. Restores preserve permissions, xattrs, ACLs, and (where possible) ownership.

bash
# Identify the snapshot
backup=$(tmutil latestbackup)
echo "$backup"

Output:

text
/Volumes/.timemachine/8B2F…/2026-05-25-094211.backup/2026-05-25-094211.backup
bash
# Restore a single file from that snapshot
sudo tmutil restore \
    "$backup/Users/alice/Documents/report.pdf" \
    "/Users/alice/Desktop/report-restored.pdf"

Output:

text
Restored: /Users/alice/Desktop/report-restored.pdf
bash
# Restore an entire directory recursively
sudo tmutil restore \
    "$backup/Users/alice/Documents/proj" \
    "/Users/alice/Desktop/proj-restored"

Output:

text
Restored: /Users/alice/Desktop/proj-restored/...

A common pattern is to mount an older snapshot and pull from it instead of the latest.

bash
# Pick a backup from 3 days ago
older=$(tmutil listbackups | awk -F/ -v cutoff="2026-05-22" '$NF ~ /^[0-9-]+\.backup$/ && $NF < cutoff"-235959.backup" {p=$0} END{print p}')
echo "$older"

Output:

text
/Volumes/.timemachine/.../2026-05-22-210112.backup/2026-05-22-210112.backup

Comparing the live filesystem against a backup (compare)

tmutil compare shows what would be added, removed, or modified if you backed up right now against the most recent snapshot. It's the fastest way to know "what's changed since my last backup" without running the backup.

bash
sudo tmutil compare -s

Output:

text
Comparing volumes...
…
+    1.2 MB  /Users/alice/Documents/notes.md
-   12.4 KB  /Users/alice/.zsh_history
~   83.0 KB  /Users/alice/Library/Mail/V10/...
        1.2 MB added
       12.4 KB removed
       83.0 KB modified
       1.2 MB total changes

The flags: -s (size summary), -c (count summary), -a (everything), -X PATH (exclude a path from comparison).

Compare two specific backups against each other.

bash
b1=$(tmutil listbackups | tail -2 | head -1)
b2=$(tmutil latestbackup)
sudo tmutil compare "$b1" "$b2" -s | head -20

Output:

text
+   3.2 MB  /Users/alice/Code/jockey/dist
~   18.4 KB /Users/alice/Code/jockey/src/...
       3.2 MB added
      18.4 KB modified

Integrity verification (verifychecksums)

Time Machine stores a checksum manifest for every file. verifychecksums (10.13+) walks the manifest and re-hashes each file to catch silent corruption.

bash
sudo tmutil verifychecksums /Volumes/.timemachine/8B2F…/2026-05-25-094211.backup

Output:

text
Verifying backup at /Volumes/.timemachine/.../2026-05-25-094211.backup
Files verified: 184231 / 184231
Errors: 0

Schedule this monthly on important destinations; it catches bit-rot before you need to restore.

Sizing and disk usage (listsizes, uniquesize, machinedirectory)

listsizes walks every backup and reports its full and unique (new-only) size. Useful for "how much of my backup is actually new data this week."

bash
sudo tmutil listsizes /Volumes/.timemachine/8B2F…/

Output:

text
   3.2 GB  2026-05-23-211201.backup
 124 MB    2026-05-24-001003.backup
 312 MB    2026-05-25-094211.backup
   3.6 GB  total

uniquesize reports the unique data for a single backup or directory.

bash
sudo tmutil uniquesize "$(tmutil latestbackup)"

Output:

text
312 MB    /Volumes/.timemachine/.../2026-05-25-094211.backup

machinedirectory returns the path that holds backups for the current Mac (the per-machine subfolder of the destination root).

bash
tmutil machinedirectory

Output:

text
/Volumes/Backup/Backups.backupdb/Alice's MacBook Pro

How tmutil interacts with sleep and power

Time Machine backups run as the backupd daemon, scheduled by launchd (com.apple.backupd-auto). The schedule is currently every hour while plugged in, plus Power Nap maintenance wakes when configured. Two pmset settings matter most.

bash
# 1. Power Nap on AC — needed for backups during sleep
sudo pmset -c powernap 1

# 2. tcpkeepalive — keep network alive during sleep for SMB Time Capsule
sudo pmset -c tcpkeepalive 1

Output: (none — exits 0)

Conversely, on battery you usually want backups not to run during sleep.

bash
sudo pmset -b powernap 0

Output: (none — exits 0)

When triggering a long unattended backup, wrap it in caffeinate -i -w so the system stays awake until backupd exits — see the caffeinate cheatsheet for the pattern.

bash
tmutil startbackup --auto --rotation
pid=$(pgrep -x backupd)
caffeinate -i -w "$pid"

Output: (none — blocks until backupd finishes, then exits 0)

Common pitfalls

  1. tmutil status blocks if no backup is running but a destination is mounting. On slow network destinations the call can hang for 30+ seconds. Wrap with gtimeout 10 tmutil status (from coreutils) if non-blocking inspection is required.
  2. startbackup without --block returns immediately. Scripts that check $? will see exit 0 even if the backup later fails. Always use --block when waiting for a result.
  3. addexclusion requires Full Disk Access for the terminal, not just sudo. Otherwise it returns Could not get exclusion even as root. Grant via System Settings → Privacy & Security.
  4. Local snapshots are kept ~24 hours. Don't rely on them as the only backup; they exist to bridge gaps, not replace off-machine destinations.
  5. deletelocalsnapshots takes the date, not the full label. tmutil deletelocalsnapshots com.apple.TimeMachine.2026-05-25-080000.local fails; use 2026-05-25-080000.
  6. thinlocalsnapshots units are bytes. A frequent mistake is to write tmutil thinlocalsnapshots / 20 1 thinking it's GB; it's bytes, so use 21474836480 for 20 GB.
  7. Path-based vs sticky exclusions behave differently. Sticky exclusions (default) follow the file; if you move or replace the file the exclusion disappears. Path-based exclusions (-p) follow the path. Inspect both with tmutil isexcluded and defaults read /Library/Preferences/com.apple.TimeMachine SkipPaths.
  8. Restoring with tmutil restore keeps original permissions. A file owned by _spotlight will be restored owned by _spotlight even if the destination user can't read it. Use chown post-restore.
  9. Network destinations can fail silently on Wi-Fi sleep. pmset -c tcpkeepalive 1 networkoversleep 1 is critical for SMB-backed Time Capsule destinations; otherwise backups appear to "skip" overnight.
  10. backupd will pause if the destination disk is under 20% free. Time Machine refuses to start a new backup when the destination is below 20%; prune old backups manually with tmutil delete on a per-backup path.
  11. APFS destinations can't be read by macOS < 10.13. A Time Machine drive formatted on macOS 11+ is unreadable on older systems. Use HFS+ for cross-version restores or keep two destinations.
  12. tmutil restore does not merge. It overwrites the destination directory wholesale. To merge selectively, restore into a scratch directory and use rsync -aH --delete-after (rsync cheatsheet).
  13. backupd itself ignores tmutil disable if a backup is in progress. Use tmutil stopbackup first, then tmutil disable, otherwise the in-flight backup runs to completion.
  14. First backups to a fresh destination can take many hours. Pair with caffeinate -i -t $((12*3600)) and pmset -c sleep 0 on AC to prevent overnight sleep from interrupting the first run.
  15. APFS encrypted destinations require the password at mount. Scripted backups must mount the destination first; otherwise startbackup fails with "destination not available". Use diskutil apfs unlockVolume with the FileVault password before starting.
  16. macOS Tahoe 26 is more aggressive about local snapshots. Community reports (and Apple Discussions threads through 26.1 / 26.2) document Time Machine backups stalling to single-digit MB/h after the initial copy when free space on the boot volume drops below ~15–20%. The mechanism: Tahoe retains more hourly local snapshots and reclaims purgeable space later than Sequoia did, so a backup that worked fine on macOS 15 may grind to a halt after upgrading. Keep at least 15–20 % of the internal SSD genuinely free; pre-emptively sudo tmutil thinlocalsnapshots / $((30*1024*1024*1024)) 1 (free 30 GB) before kicking off the first post-upgrade backup; if backups are already stuck, delete all local snapshots and let the next run recreate a clean set. See Eclectic Light's Tahoe Time-Machine series and the Apple Community threads in Sources for the current state.

Real-world recipes

Manual "before risky change" backup

A typical "I'm about to upgrade macOS" pattern: force a backup, wait for it, snapshot locally as a second safety net.

bash
tmutil startbackup --block --auto
tmutil localsnapshot
tmutil latestbackup

Output:

text
Backup completed successfully.
Created local snapshot with date: 2026-05-25-103011
/Volumes/.timemachine/.../2026-05-25-103011.backup/2026-05-25-103011.backup

Daily incremental run with sleep prevention

A cron-like script that triggers a backup, holds the system awake, and logs the result.

bash
#!/bin/bash
set -euo pipefail
log=/Users/alice/.local/share/tm-runs.log
{
    echo "--- $(date -Iseconds) start"
    tmutil startbackup --block --auto --rotation
    echo "--- $(date -Iseconds) latest=$(tmutil latestbackup)"
} | tee -a "$log"

Output:

text
--- 2026-05-25T22:00:00 start
Backup completed successfully.
--- 2026-05-25T22:18:42 latest=/Volumes/.timemachine/.../2026-05-25-220011.backup/2026-05-25-220011.backup

Auto-thin local snapshots when disk is low

Detect "< 20% free" and free 20 GB by thinning.

bash
#!/bin/bash
threshold=20
pct=$(df / | awk 'NR==2 {gsub("%",""); print 100-$5}')
if (( pct < threshold )); then
    echo "Disk at ${pct}% free — thinning snapshots."
    sudo tmutil thinlocalsnapshots / $((20*1024*1024*1024)) 1
fi

Output:

text
Disk at 14% free — thinning snapshots.
Thinned local snapshots:
2026-05-24-100000
2026-05-24-110000
…

Find every excluded path on this Mac

Sticky exclusions are stored as an xattr; path-based exclusions are in the plist.

bash
# Sticky exclusions — slow, walks the disk
sudo mdfind 'kMDItemFSContentChangeDate > $time.today(-30) && kMDItemPath != "/System"' \
    | head -200 \
    | while read p; do
        if tmutil isexcluded "$p" 2>/dev/null | grep -q "\[Excluded\]"; then
            printf "sticky\t%s\n" "$p"
        fi
      done

# Path-based exclusions — direct read
defaults read /Library/Preferences/com.apple.TimeMachine SkipPaths

Output:

text
sticky    /Users/alice/Code/proj/node_modules
(
    "/Users/alice/Downloads",
    "/private/var/folders"
)

Surface the wear curve of a backup destination

listsizes plus awk give a quick chart of per-backup growth.

bash
sudo tmutil listsizes "$(tmutil machinedirectory)" \
    | awk '/backup/ {printf "%-30s %s\n", $NF, $1$2}'

Output:

text
2026-05-23-211201.backup       3.2GB
2026-05-24-001003.backup       124MB
2026-05-25-094211.backup       312MB

Restore a file deleted yesterday

bash
backup=$(tmutil listbackups | awk -F/ -v cutoff="2026-05-24-235959" '$NF < cutoff".backup" {p=$0} END{print p}')
sudo tmutil restore \
    "$backup/Users/alice/Documents/deleted.txt" \
    /Users/alice/Desktop/deleted.txt

Output:

text
Restored: /Users/alice/Desktop/deleted.txt

Bash-driven "rotate destination then verify"

A weekly job that swaps between two physical drives and checksum-verifies the result.

bash
#!/bin/bash
set -e
disk_a="/Volumes/Backup-A"
disk_b="/Volumes/Backup-B"
today=$(date +%u)         # 1-7
if (( today == 7 )); then
    target="$disk_b"
else
    target="$disk_a"
fi
[[ -d "$target" ]] || { echo "$target missing"; exit 1; }
sudo tmutil setdestination "$target"
tmutil startbackup --block --auto
sudo tmutil verifychecksums "$(tmutil latestbackup)"

Output:

text
Verifying backup at /Volumes/Backup-A/Backups.backupdb/.../2026-05-25-094211.backup
Files verified: 184231 / 184231
Errors: 0

Pre-flight checks before macOS upgrade

bash
set -e
# 1. Make sure a destination is configured and reachable
tmutil destinationinfo | grep -q "Mount Point" || { echo "No destination."; exit 1; }
# 2. Force a current backup
tmutil startbackup --block --auto
# 3. Pin a local snapshot
tmutil localsnapshot
# 4. Verify the latest backup
sudo tmutil verifychecksums "$(tmutil latestbackup)"
# 5. Disable scheduled backups during the upgrade
sudo tmutil disable

Output: (each line announces success; final state: Time Machine paused, fresh backup verified, local snapshot pinned)

Mass-exclude common developer caches

These directories regenerate from source, so backing them up wastes destination space.

bash
for p in \
    /Users/alice/Code/*/node_modules \
    /Users/alice/Code/*/__pycache__ \
    /Users/alice/Code/*/.venv \
    /Users/alice/Library/Caches/Yarn \
    /Users/alice/Library/Caches/pip ; do
    sudo tmutil addexclusion "$p"
done

Output: (none per line — exits 0; subsequent backups skip these paths)

Restoring permissions properly

After tmutil restore the file is owned as it was at backup time — often the wrong owner if you restored across machines. Normalize ownership.

bash
sudo tmutil restore "$backup/Users/alice/Code/proj" /Users/alice/Desktop/proj-restored
sudo chown -R alice:staff /Users/alice/Desktop/proj-restored

Output: (none — exits 0)

Build a free-disk dashboard

Sample the destination size and the source disk's used % into a CSV.

bash
out=/Users/alice/.local/share/tm-disk.csv
ts=$(date -Iseconds)
dest_used=$(df -k "$(tmutil destinationinfo | awk -F': ' '/Mount Point/ {print $2; exit}')" \
            | awk 'NR==2 {print $3}')
src_free=$(df -k / | awk 'NR==2 {print $4}')
printf "%s,%d,%d\n" "$ts" "$dest_used" "$src_free" >> "$out"

Output: (one CSV row appended; over time → growth trend)

Cancel a stuck backup and clear local state

When a network backup hangs for hours, the cleanest recovery is to stop, prune in-progress markers, and rerun.

bash
tmutil stopbackup
sudo killall backupd 2>/dev/null || true
sudo rm -f "$(tmutil machinedirectory)"/*.inProgress
tmutil startbackup --block --auto

Output: (none — exits 0; backup restarts cleanly)

Reinstate Time Machine on a replacement Mac

After migrating to a new MacBook, point the new machine at the existing backup history.

bash
# Mount the old destination
diskutil mount disk5s1                                # if it's an external drive
# Or:
mkdir -p /Volumes/TM-Network
mount_smbfs //timemachine:secret@nas.local/TM /Volumes/TM-Network

# Tell Time Machine to inherit the prior backup history
sudo tmutil inheritbackup "$(tmutil machinedirectory)"
sudo tmutil setdestination /Volumes/Backup

Output:

text
The backup directory has been claimed by the local machine.

The new Mac now sees the prior backup history; future backups append to it instead of starting a fresh history.

Audit which directory is bloating the backup

Use compare -s to find churn-heavy directories that are forcing big incremental sizes every hour.

bash
sudo tmutil compare -s 2>/dev/null \
    | awk '/^[+~-]/' \
    | awk '{print $2"\t"$NF}' \
    | sort -hr \
    | head -10

Output:

text
312 MB   /Users/alice/Library/Caches/com.apple.iCloud
84 MB    /Users/alice/Code/jockey/dist
21 MB    /Users/alice/Library/Mail/V10

Each line is a candidate for tmutil addexclusion.

  • diskutil — format and inspect the APFS volumes that host both backups and the source Data volume. The diskutil apfs listsnapshots command shows the same local snapshots from a different angle.
  • pmset — controls Power Nap and sleep behaviour that determines when scheduled backups actually run.
  • caffeinate — wrap long unattended backups to prevent the host from sleeping mid-transfer.
  • rsync — manual incremental sync alternative when Time Machine is unsuitable (cross-platform destinations, headless servers, snapshot-style hard-linked backups).
  • backupd — the underlying daemon; surfaces in /var/log/system.log via the com.apple.backupd subsystem. Investigate problems with log show --predicate 'subsystem == "com.apple.backupd"' --info --last 1h.
  • mount_smbfs / mount_afp — mounting the network Time Machine share before tmutil can write to it.

Sources

References consulted while writing and updating this article. Links open in a new tab. Order is curated by relevance — sorting is intentionally disabled (data-no-sort).

SourceWhy cited
Time Machine backup slows after Tahoe upgrade — Apple CommunityPrimary user-report thread documenting the Tahoe-specific snapshot stall, used as the basis for the new pitfall #16 about aggressive local-snapshot retention.
Time Machine doing full backups on macOS Tahoe — Apple CommunityCross-confirmation of the post-upgrade full-backup behaviour and the recommended remediation (free 15–20 % of the boot volume, prune snapshots).
Why are these files created since installing Tahoe 26.1? — Apple CommunityBackground on the additional com.apple.TimeMachine.*.local snapshots that appear on Tahoe 26.1+ which inform the thinning advice in pitfall #16.
How to delete Time Machine local snapshots in macOS — AppleInsiderCanonical syntax reference for tmutil listlocalsnapshots / deletelocalsnapshots used throughout Local snapshots.
How to remove com.apple.TimeMachine.*.bac files — Apple CommunityBackground on the .inProgress and *.bac sidecar files referenced in the Cancel a stuck backup recipe.