A workaround and diagnosis guide for a memory leak in Backblaze Personal Backup's
bzserv process that quietly fills macOS swap until the system runs out of
application memory.
- Activity Monitor shows Backblaze using tens of GB of memory.
sysctl vm.swapusagereports swap climbing into the 20–60 GB range over a few days of uptime.- macOS eventually surfaces "Your system has run out of application memory."
ps -o rss -p "$(pgrep -x bzserv)"looks fine (~100 MB). The leak is hidden in compressed + swapped-out dirty pages that don't show up in RSS.
Observed on Backblaze 10.0.1.1038 on macOS 26 (Apple Silicon).
Earlier 9.x had a separate but related problem: Backblaze's frequent
.tmp → rename writes under /Library/Backblaze.bzpkg/bzdata/bzreports/
caused fseventsd/Spotlight to bloat. Upgrading to 10.x fixed that one and
introduced this MALLOC_SMALL leak in bzserv itself.
Activity Monitor's "Memory" column shows phys_footprint, not RSS.
That's why ps disagrees — you need the Mach APIs:
PID="$(pgrep -x bzserv)"
sudo footprint "$PID" | head -20
sudo vmmap -summary "$PID" | head -30A leaking bzserv shows a huge MALLOC_SMALL region with nearly 100% of it
swapped out, e.g.:
Physical footprint: 49.0G
Writable regions: Total=49.1G written=49.0G(100%) resident=27.6M(0%) swapped_out=49.0G(100%)
MALLOC_SMALL 48.8G 1968K ... 12495 regions
Observed rate: ~500 MB/hour, ~12 GB/day of small allocations that are never freed. They get compressed/swapped aggressively, so the cost is invisible in most tools but saturates swap after a few days.
Graceful restart (only bzserv is recycled; bztransmit and bzfilelist
are untouched; launchd respawns it):
sudo launchctl kickstart -k system/com.backblaze.bzserv
sudo purge # nudges VM to drop clean cached pages; swap reclaims lazily
sysctl vm.swapusageVerify the new footprint:
sleep 3
NEWPID="$(pgrep -x bzserv)"
sudo footprint "$NEWPID" | head -4Expect the new bzserv to come up with footprint in the tens of MB. Swap
won't shrink immediately — macOS holds swapfiles around and reclaims them
lazily — but pressure goes away right away.
If kickstart doesn't recover it for some reason, the hard-kill fallback
is plain pkill:
sudo pkill -9 bzserv bztransmit bzfilelist bzbmenu
# launchd will respawn bzserv; the others spin up on demandrestart-bzserv.sh — graceful bounce with before/after footprint and swap
summary. Safe to run any time.
#!/usr/bin/env zsh
set -u
echo "── Restarting bzserv ──"
OLD_PID="$(pgrep -x bzserv || true)"
if [[ -n "${OLD_PID}" ]]; then
OLD_FP="$(sudo footprint "${OLD_PID}" 2>/dev/null | awk '/Footprint:/ {print $3, $4; exit}')"
echo "Before: pid=${OLD_PID} footprint=${OLD_FP:-unknown}"
else
echo "Before: bzserv not running"
fi
sudo launchctl kickstart -k system/com.backblaze.bzserv
for _ in 1 2 3 4 5; do
NEW_PID="$(pgrep -x bzserv || true)"
[[ -n "${NEW_PID}" && "${NEW_PID}" != "${OLD_PID}" ]] && break
sleep 1
done
if [[ -z "${NEW_PID:-}" ]]; then
echo "⚠️ bzserv did not respawn within 5s"
exit 1
fi
sleep 2
NEW_FP="$(sudo footprint "${NEW_PID}" 2>/dev/null | awk '/Footprint:/ {print $3, $4; exit}')"
echo "After: pid=${NEW_PID} footprint=${NEW_FP:-unknown}"
sudo purge
echo
echo "── Swap status ──"
sysctl vm.swapusage | sed 's/^/ /'
echo
echo "✓ bzserv restarted"A daily launchd job keeps the leak bounded. Pick a label in your own
reverse-DNS namespace. This example uses com.example.restart-bzserv; adjust
as needed.
/Library/LaunchDaemons/com.example.restart-bzserv.plist:
<?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>Label</key>
<string>com.example.restart-bzserv</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>/path/to/restart-bzserv.sh</string>
</array>
<!-- Daily at 03:10 local. Avoid 03:00 sharp so we don't collide with
all the other on-the-hour scheduled jobs. -->
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key><integer>3</integer>
<key>Minute</key><integer>10</integer>
</dict>
<key>RunAtLoad</key><false/>
<key>StartCalendarIntervalStartsImmediatelyIfLate</key><false/>
<key>ProcessType</key><string>Background</string>
<key>LowPriorityIO</key><true/>
<key>Nice</key><integer>5</integer>
<key>AbandonProcessGroup</key><true/>
<key>StandardOutPath</key><string>/var/log/restart-bzserv.log</string>
<key>StandardErrorPath</key><string>/var/log/restart-bzserv.log</string>
</dict>
</plist>Install:
sudo install -o root -g wheel -m 0644 \
./com.example.restart-bzserv.plist \
/Library/LaunchDaemons/com.example.restart-bzserv.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/com.example.restart-bzserv.plistInspect / trigger / disable:
sudo launchctl print system/com.example.restart-bzserv
sudo launchctl kickstart -k system/com.example.restart-bzserv # ad-hoc run
sudo launchctl disable system/com.example.restart-bzserv
sudo launchctl enable system/com.example.restart-bzservUninstall when Backblaze ships a fix:
sudo launchctl bootout system/com.example.restart-bzserv
sudo rm /Library/LaunchDaemons/com.example.restart-bzserv.plist
sudo rm -f /var/log/restart-bzserv.logHarmless on 10.x but useful on 9.x — prevents mds/mdworker from chasing
Backblaze's high-frequency .tmp writes under its bundle, which cascades into
fseventsd memory growth.
/Library/Backblaze.bzpkg is a bundle (kind BackblazePackage) and cannot
be dragged into System Settings → Spotlight → Privacy. Use Apple's documented
marker file instead:
sudo touch /Library/Backblaze.bzpkg/.metadata_never_index
sudo touch /Library/Backblaze.bzpkg/bzdata/.metadata_never_indexOn modern macOS, launchctl kickstart on Apple launchd jobs
(e.g. com.apple.fseventsd) is blocked by SIP (Operation not permitted).
The Backblaze job is third-party and not subject to this restriction, so
launchctl kickstart -k system/com.backblaze.bzserv works as root.
If you also need to flush a bloated fseventsd, the workaround is a signal
rather than launchctl:
sudo kill -HUP "$(pgrep -x fseventsd)"If the leak persists on a newer Backblaze release, capture state and attach to a support ticket:
sudo vmmap -summary "$(pgrep -x bzserv)" > /tmp/bzserv-vmmap.txt
sudo footprint "$(pgrep -x bzserv)" > /tmp/bzserv-footprint.txt
sw_vers
defaults read /Applications/Backblaze.app/Contents/Info CFBundleShortVersionString