Excluding Shunt data from Time Machine backups
On this page7 sections ▾
My Time Machine backup over SMB filled a fresh 4 TB destination while backing up a 2 TB Mac. I excluded the development data I can recreate, including Shunt's data volumes and the container runtime, while keeping source worktrees eligible for backup.
I now use Shunt to run parallel development environments. It uses APFS copy-on-write (CoW) cloning for their data volumes, so each environment gets its own copy of the baseline data while sharing unchanged disk blocks. That keeps local disk usage down, but a backup that stores those clones as independent copies can need much more space.
The source Data volume contained about 1.78 TB, but the backup image reached roughly 3.7 TB before Time Machine stopped at its required 100 GB free-space reserve:
Insufficient free space after thinning all available backups
free space: 98.76 GB, minimum free space: 100 GB
BACKUP_FAILED_TARGETVOL_DISK_FULL (56)#Shunt data volumes use APFS clones
Shunt creates its data-volume clones with macOS's clonefile API and mounts the host directories into its guests. With APFS cloning, changes use new blocks while the copies continue sharing unmodified blocks. Each environment can modify its database without changing the baseline or another environment's copy.
Adding up the logical sizes of those directories counts shared data more than once. Even allocated-block totals can count blocks shared between clones, so a directory scan doesn't tell you how much space deleting one copy would free. Shunt's disk-usage reporting makes that distinction too.
For backups, the question is whether the tool preserves that sharing or stores the copies separately. The local space saving alone doesn't establish how large the backup will be.
#Container disks also use sparse files
Shunt uses Apple's container runtime for its guests. The runtime's sparse ext4 disk images save space by leaving empty regions unallocated. APFS clones share existing data between files; sparse files leave holes within a file.
On my Mac, the container runtime state was here:
~/Library/Application Support/com.apple.containerUse du to total the allocated blocks reported for the files:
du -x -sh "$HOME/Library/Application Support/com.apple.container"154G /Users/.../Library/Application Support/com.apple.containerAdd -A to measure the apparent size:
du -x -A -sh "$HOME/Library/Application Support/com.apple.container"19T /Users/.../Library/Application Support/com.apple.containerThese figures describe the container runtime directory, not Shunt's host data volumes. The large difference is consistent with the sparse disk images in that directory. One of my rootfs.ext4 files was 513 GiB logically but used about 18 GiB of blocks. Others were 512 GiB logically while using less than 1 GiB locally.
An issue in Apple's container repository reports sparse images taking up much more space when backed up with Time Machine over SMB. That is consistent with the backup growth I saw, but these directory measurements don't establish how much of my failed backup came from sparse images versus cloned data. The 19 TB apparent total isn't a measurement of the backup itself.
#Choose which data to exclude
The Apple Container runtime has guest root filesystems, snapshots, image blobs and BuildKit state. I can recreate mine, so I don't include them in Time Machine.
In the setup I was investigating, Shunt kept its cloned data volumes under paths shaped like this:
~/Developer/github/<org>/.shunt-dev/<project>/<siding>/volThose vol folders can contain cloned local databases or other environment data. Mine are reproducible, so I exclude them too. If your local database contains test state you can't recreate, arrange a separate backup before excluding it.
I do not blanket-exclude every .shunt-dev directory. A siding source worktree may contain uncommitted work, so source and runtime data should not be treated as the same thing.
#Apply and verify the exclusions
You can manage exclusions through System Settings > General > Time Machine > Options. Apple documents that flow in Exclude files from a Time Machine backup on Mac.
To exclude the container runtime from the terminal:
runtime="$HOME/Library/Application Support/com.apple.container"
tmutil addexclusion "$runtime"
tmutil isexcluded "$runtime"The check should return:
[Excluded] /Users/.../Library/Application Support/com.apple.containerFor a Shunt data volume you can recreate, use the same commands with its specific path. Replace the example below with the volume directory you checked:
volume="/path/to/siding/vol/database"
tmutil addexclusion "$volume"
tmutil isexcluded "$volume"Check for [Excluded] for each selected volume. Keep the exclusion on the data directory so it doesn't also exclude the siding's source worktree.
The plain addexclusion form creates a sticky exclusion that follows the directory if it moves. Fixed-path exclusions use -p and need elevated privileges; the sticky form is enough for this directory.
Excluded items still appear in local Time Machine snapshots, as Apple's documentation notes. If you delete a large excluded folder and Finder still shows the old free-space number, an existing local snapshot may be retaining those blocks until Time Machine finishes or macOS purges the snapshot.
#Exclude generated build folders
I also exclude generated files from my development projects: .NET creates bin and obj, Rust creates target, JavaScript projects create node_modules, SwiftPM creates .build, and Xcode has Derived Data and Simulator state.
Time Machine does not support an exclusion glob, so I keep a small config-driven script that scans my development roots and marks the real directories. The core operation can be as small as this:
find "$HOME/Developer" \
-type d \( \
-name bin -o \
-name obj -o \
-name target -o \
-name node_modules -o \
-name .build \
\) -prune -print0 \
| xargs -0 -n 100 tmutil addexclusionMy version first checks the exclusion xattr and sends only new paths to tmutil. This avoids repeatedly changing metadata on thousands of already-excluded directories and creating extra work for Spotlight.
I also run it daily and clean exact src-tauri/target directories through Cargo:
cargo clean \
--manifest-path /path/to/src-tauri/Cargo.toml \
--target-dir /path/to/src-tauri/targetAfter cleaning, the script recreates the empty target directory and reapplies its Time Machine exclusion. Otherwise cargo clean can remove the directory along with the sticky xattr, and the next build becomes eligible for backup again.
#Check how other backup tools store the data
Sparse-file support and clone sharing need separate checks. Rsync's --sparse option, for example, tries to store runs of zero bytes without allocating blocks for them. Its result depends on the destination filesystem and kernel support. That option alone doesn't preserve the shared blocks between APFS clones.
For cloned data volumes, check whether the backup tool preserves clone relationships or deduplicates repeated data. For sparse disk images, check whether it preserves holes or otherwise stores empty regions efficiently. Measure the destination usage with a representative backup; source directory sizes alone don't account for either behaviour.
#Backup progress after the exclusions
After excluding Apple Container and the reproducible Shunt data stores, I cleared the failed destination and started again. At about 26% through, the replacement backup had used 630 GiB and still had roughly 3 TiB free. Time Machine estimated about 1.55 TB of eligible source data instead of the earlier 1.78 TB.
The backup was still running when I captured those figures. They show progress after excluding both storage layers, but the final backup size was still unconfirmed.