Figment.so
Mac hostingWebsite hostingGuidesHow to usePricing

Handle Disk Pressure on a Hosted Automation Mac

Disk pressure is a silent automation killer. A download fills the drive, df returns 100%, and the next write fails with no stack trace in the job log — just a stalled process and a confused on-call engineer. On a hosted Mac with a fixed disk, the right response is a written storage budget, a cleanup allowlist that explicitly names what is safe to delete, and a scheduled check that fires before the disk is actually full.

Which Figment plan fits this job

Figment's $29/month virtual Intel macOS environment ships with 2 vCPU, 8 GiB RAM and an 80 GiB disk. That is enough for most automation that processes documents, calls APIs, or scrapes moderate volumes. If your jobs download large media, cache multi-gigabyte artifacts, or keep rolling browser profiles, 80 GiB fills quickly — especially once system logs and build caches accumulate between runs.

The VM is Intel, runs on Linux/KVM, and has no GPU acceleration or audio. It can run compatible Intel macOS toolchains, including Xcode 26 or earlier for cross-compiling supported targets. If you need Apple silicon execution, a larger disk, or more RAM, request an M-series Mac from $95/month and confirm the exact chip, RAM, and disk in your quote. Physical plans and i3 are request-based until supply is confirmed.

Start a configuration at /mac-hosting/start.

Map the disk consumers and write the allowlist

Before writing any cleanup code, list who writes to disk and classify each path. A short table beats a shell wildcard that eats project data by accident.

DirectoryTypical sizeSafe to auto-delete?
~/Downloads/VariesYes, after the job validates each download
~/Library/Caches/2–10 GiBYes, caches rebuild on demand
/Library/Logs/, ~/Library/Logs/500 MB–2 GiBYes, truncate or rotate
~/Library/Developer/Xcode/DerivedData/10–40 GiBYes, rebuilds on next build
~/workdir/ (your project root)Depends on projectNo — protect this
~/workdir/checkpoints/SmallNo — required for crash resume
~/workdir/artifacts/VariesOnly after upload is confirmed

The script cleans from this allowlist only. It never traverses the whole filesystem looking for large files, because that risks deleting project data, provisioning profiles, or keychain material. It runs as your agent user and never needs root for these paths.

Implement the storage budget and cleanup

Save as /usr/local/bin/disk-budget-check and make it executable with chmod 755.

#!/usr/bin/env bash
# Checks free space, logs usage, and cleans allowlisted paths when
# free space falls below the warning or critical thresholds.
set -euo pipefail

WARN_GIB=15      # Alert at this many GiB free
CRITICAL_GIB=5   # Clean at this many GiB free
LOG="$HOME/logs/disk-budget.log"
mkdir -p "$(dirname "$LOG")"

free_gib() { df -g "$HOME" | awk 'NR==2 {print $4}'; }
log()      { echo "$(date -u +%FT%TZ) $*" | tee -a "$LOG"; }

FREE=$(free_gib)
log "Free: ${FREE} GiB"

if (( FREE >= WARN_GIB )); then
    log "OK: above warning threshold."
    exit 0
fi

log "WARNING: ${FREE} GiB free is below ${WARN_GIB} GiB."
log "--- Top consumers ---"
du -shx \
    "$HOME/Downloads" \
    "$HOME/Library/Caches" \
    "$HOME/Library/Logs" \
    "$HOME/Library/Developer/Xcode/DerivedData" \
    2>/dev/null | sort -h | tee -a "$LOG"

if (( FREE >= CRITICAL_GIB )); then
    log "Above critical threshold; alerting only."
    exit 1   # Non-zero so a monitoring wrapper can send a notification
fi

log "CRITICAL: ${FREE} GiB free. Running allowlist cleanup."

cleanup_path() {
    local path="$1"
    [[ -d "$path" ]] || return 0
    log "Cleaning: $path"
    find "${path:?}" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
}

cleanup_path "$HOME/Downloads"
cleanup_path "$HOME/Library/Caches"
cleanup_path "$HOME/Library/Logs"
cleanup_path "$HOME/Library/Developer/Xcode/DerivedData"

FREE_AFTER=$(free_gib)
log "Free after cleanup: ${FREE_AFTER} GiB"

if (( FREE_AFTER < CRITICAL_GIB )); then
    log "FAIL: still below critical after cleanup; needs manual review."
    exit 2
fi

log "Cleanup complete."

find … -mindepth 1 -maxdepth 1 -exec rm -rf {} + removes only direct children of the named directory and handles empty directories cleanly. The ${path:?} expansion aborts the script if the variable is ever empty, which is the belt-and-braces guard against deleting at /. Review it against your own project layout before running — this is an example, not a reported benchmark.

Schedule the check with launchd

Agent property-list files live in ~/Library/LaunchAgents/. Save this as com.yourorg.disk-budget.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.yourorg.disk-budget</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/disk-budget-check</string>
    </array>
    <key>StartInterval</key>
    <integer>1800</integer>
    <key>StandardOutPath</key>
    <string>/Users/agent/logs/disk-budget.out.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/agent/logs/disk-budget.err.log</string>
</dict>
</plist>

Load it with launchctl load ~/Library/LaunchAgents/com.yourorg.disk-budget.plist. StartInterval is documented in Apple's Creating Launchd Jobs reference, and the agent/daemon distinction and session behavior are in Technical Note TN2083. On the $29 VM you reach the shell through the browser console terminal; there is no inbound SSH, so run launchctl load interactively from that terminal.

Verify the script before a production run

Three checks are enough to trust the schedule:

  1. Clean-disk path. Run with the thresholds inverted so it exits early: WARN_GIB=1 CRITICAL_GIB=0 /usr/local/bin/disk-budget-check. Expect an OK line.
  2. Allowlist safety. Create ~/Downloads/test-cleanup/ and ~/workdir/important-data/, then run with very high thresholds so cleanup always fires. Confirm ~/workdir/important-data/ is untouched. This is the one test you cannot skip.
  3. Launchd fires on schedule. launchctl list | grep disk-budget should show the label with PID - and last exit 0. Force a run with launchctl start com.yourorg.disk-budget and tail the log.

After large jobs, call df -h ~ from the browser console terminal before you queue the next one. If the disk sits under 15 GiB free for several checks in a row, that is the signal to resize — not a cue to raise CRITICAL_GIB.

Cost and when to move up a plan

The 80 GiB disk on the $29 VM is a fixed allocation; there is no on-demand volume expansion in the current offer. Worked example: a nightly job that downloads 6 GiB, writes 2 GiB of derived data, and leaves 1 GiB of logs touches 9 GiB per run. Starting from 75 GiB of usable headroom (80 GiB disk minus the 5 GiB critical reserve), that is 75 / 9 ≈ 8 runs before you cross the threshold. With the allowlist above, each run returns to roughly the same free-space baseline and the plan holds.

PlanDiskFits this job when
Virtual Intel macOS — $29/mo80 GiB (confirmed)Light-to-medium automation, API calls, document processing
M-series Mac — from $95/moConfirm in quoteLarger working set, ARM execution, or more RAM
Managed Build Mac — $249/moConfirm in quoteOne M-series Mac with CI setup, backups, monitoring, and 30 minutes monthly support

Managed Build Mac adds backups and monitoring on top of a single M-series Mac, which pairs well with the in-process check above: the external monitor catches a stuck machine, the launchd job catches a stuck disk. Pricing, chip, RAM, and disk for physical M-series are confirmed in the quote.

Start or migrate the workload

Request a configuration at Figment Mac Hosting and walk through /mac-hosting/start. Once the Mac is ready:

  1. Install /usr/local/bin/disk-budget-check and chmod 755 it.
  2. Drop the plist into ~/Library/LaunchAgents/ and launchctl load it.
  3. Run checks 1 and 2 above before the next production job.

To migrate an existing workflow, run du -shx ~/* 2>/dev/null | sort -h on the old host and confirm the allowlist covers the same categories before cutover. Caches on the new host start empty, which is the right moment to prove the allowlist is complete.


Related guides:


Subscribe for Figma design and website publishing guides.


Figment.so

Contact

Twitter

Website pricing

Compute index

About

Privacy

Terms