Figment.so
Mac hostingWebsite hostingGuidesHow to usePricing

Plan macOS Updates Around Hosted Agent Jobs

A macOS update applied at the wrong moment terminates in-flight jobs, changes tool versions, and can break a dependency chain your automation has relied on for months. The fix is not to skip updates — it is to control when they happen and to have a tested rollback. This page gives you that runbook.

for a single long-running agent, run on the Figment virtual Intel macOS environment at $29/month and own the update cadence from the browser console. Teams that want a rotation (one Mac updating while the other keeps jobs moving) should price the $999 Managed Mac Team plan: two M-series Macs, two CI runners, team setup, backups, monitoring, and one hour of monthly support. Physical i3 ($49) and M-series (from $95) plans collect requests until supply is confirmed, so quote the exact chip/RAM/disk you need before committing.

Choose a Mac plan and define the job

Pick the plan before you plan the maintenance window — the update path differs for each.

Figment planMonthlyUpdate path
Virtual Intel macOS (2 vCPU, 8 GiB RAM, 80 GiB disk)$29You run softwareupdate from the VM's browser-console desktop terminal
Physical i3 Mac$49Request-based; coordinate the window with Figment
Physical M-series Macfrom $95Request-based; confirm chip/RAM/disk and window in the quote
Managed Build Mac (one M-series, CI setup, backups, monitoring, 30 min support)$249Discuss the window with Figment; monitoring will flag a stuck agent
Managed Mac Team (two M-series, two CI runners, team setup, backups, monitoring, 1 hr support)$999Rotate: update one node while the other keeps running

The $29 VM runs macOS on Linux/KVM with no GPU acceleration or audio. It is Intel, so it will run compatible Intel Xcode 26-or-earlier toolchains for cross-compiled arm64 builds and most CLI-driven agent work; choose an M-series plan if you need native ARM execution, Xcode 27, visionOS, or GPU-bound tasks (Apple: porting your macOS apps to Apple silicon).

Inputs and required permissions

A safe update needs three artifacts ready before you touch anything:

  • A known-good checkpoint — installed tool versions, running services, and agent config you can diff against after the update.
  • A drain signal — a mechanism that tells the agent to finish the current job and not start another.
  • A rollback target — either an external snapshot you can restore, or a fully scripted re-provisioning path (Brewfile + agent config in version control). The $29 VM does not expose APFS snapshot management to the guest, so plan on scripted re-provisioning rather than an in-place filesystem rollback.

The simplest drain mechanism is a sentinel file the job wrapper checks at the top of each run:

#!/usr/bin/env bash
set -euo pipefail
DRAIN_FLAG="$HOME/agent/.drain"

if [[ -f "$DRAIN_FLAG" ]]; then
  echo "Drain flag set — agent will not start a new job" >&2
  exit 0
fi
# ... rest of job logic

Pair this with a launchd plist whose KeepAlive dictionary sets SuccessfulExit to false — launchd will relaunch only on a non-zero exit, so a clean exit under drain stops the restart loop. See the launchd.plist(5) man page and Apple's Creating Launchd Jobs for the exact plist schema.

The maintenance runbook

Work through these phases in order; each one has an explicit pass check before you proceed.

Phase 1 — Drain

  1. touch ~/agent/.drain
  2. Watch the current job complete: tail -f /tmp/agentjob.log.
  3. Confirm launchd shows the job idle: launchctl list | grep agentjob — the PID column should be -.
  4. Notify upstream schedulers and webhooks that the agent is offline for the window.

Phase 2 — Checkpoint

Record the environment so you know exactly what to restore:

#!/usr/bin/env bash
set -euo pipefail
OUT="$HOME/agent/checkpoint-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$OUT"
sw_vers                        > "$OUT/os-version.txt"
xcode-select -p                > "$OUT/xcode-path.txt"   || true
brew list --versions           > "$OUT/brew-versions.txt"
brew bundle dump --file="$OUT/Brewfile" --force
launchctl list                 > "$OUT/launchctl-list.txt"
ln -sfn "$OUT" "$HOME/agent/checkpoint-latest"
echo "Checkpoint saved to $OUT"

Copy the checkpoint directory to storage outside the VM (object storage, your own server, or a git repo you control) before updating. Do not place secret values in the checkpoint; capture only names and versions.

Phase 3 — Update

Apply the update from the browser-console terminal. The hosted Mac is running headlessly, so the terminal session drives the installer:

# List available updates
softwareupdate --list

# Install all recommended updates (patches only — no major macOS upgrade)
sudo softwareupdate --install --recommended --restart

# Or install one by exact label from --list
sudo softwareupdate --install "macOS Sequoia 15.x.x-..." --restart

--recommended applies security and reliability patches without triggering a major macOS version jump. A major upgrade on a hosted VM should be a deliberate decision, not a side effect — confirm your Intel-compatible Xcode/toolchain still supports the target macOS before you take it. If your organization pushes updates through MDM instead of the CLI, check Apple's current MDM deferral documentation before relying on timing you tested on an earlier macOS.

Phase 4 — Post-update verification

After the machine comes back up, run a verification script before removing the drain flag:

#!/usr/bin/env bash
set -euo pipefail

CP="$HOME/agent/checkpoint-latest"

echo "=== OS version ==="
sw_vers

echo "=== Brew bundle check ==="
brew bundle check --file="$CP/Brewfile" \
  || { echo "FAIL: missing Homebrew packages"; exit 1; }

echo "=== Agent smoke test ==="
# Use your real smoke test — do not count a failed build as a success.
shortcuts run "AgentSmokeTest" \
  || { echo "FAIL: smoke test"; exit 1; }

echo "All checks passed"

The shortcuts command is documented by Apple at Run shortcuts from the command line. Replace AgentSmokeTest with a shortcut or script that exercises the same code paths your production job does, including any network call your agent depends on. If the script exits non-zero, do not remove the drain flag — move to rollback.

Phase 5 — Resume or rollback

Resume: rm ~/agent/.drain. The next launchd trigger starts the agent again.

Rollback if verification fails:

SituationAction
Brew packages brokenbrew bundle install --file="$HOME/agent/checkpoint-latest/Brewfile"
Tool version regressedRe-install via Brewfile or brew install formula@version
Core dependency broken by OS changeRe-provision a fresh Mac and restore agent config from the checkpoint
Cannot restore in placeRequest a new VM or physical Mac from Figment and replay the checkpoint

Runbook at a glance

1. Drain       touch ~/agent/.drain
               wait for the running job to finish

2. Checkpoint  ./save-checkpoint.sh
               copy checkpoint dir off the host

3. Update      sudo softwareupdate --install --recommended --restart

4. Verify      ./verify-after-update.sh
               PASS -> remove drain flag
               FAIL -> rollback; keep drain flag set

5a. Resume     rm ~/agent/.drain
5b. Rollback   brew bundle install  |  re-provision + replay checkpoint

Failure modes and recovery checks

  • Agent exits immediately after drain removed — tail -50 /tmp/agentjob.err; a changed PATH or a missing CLI is the usual cause.
  • Brew formula on the wrong version — brew list --versions <formula>; brew upgrade or pin with brew pin.
  • Shortcut fails after the OS update — relaunch the Shortcuts app with open -a Shortcuts, then retry.
  • launchd agent will not load — launchctl load ~/Library/LaunchAgents/com.yourteam.agentjob.plist and check log show --last 5m --predicate 'subsystem == "com.apple.launchd"'.

Cost and operating limits for the maintenance window

Billing on the $29 virtual Intel macOS plan is monthly; a maintenance window during the month does not change the invoice. Pick a natural quiet period in your schedule rather than optimizing the clock.

A small worksheet for teams considering the rotation pattern on Managed Mac Team ($999/month, two M-series Macs, two CI runners, one hour of monthly support included): if a single-node update usually takes about 90 minutes of agent downtime and you run four maintenance cycles per quarter, running on two nodes removes roughly 4 × 90 = 360 minutes of job backlog per quarter without you having to coordinate a stop-the-world window. Whether that saves money depends on your job value per hour and whether that hour of included support covers the kind of help you actually need; price it against your own backlog cost before upgrading from the $249 Managed Build Mac (one M-series Mac, CI setup, backups, monitoring, 30 minutes of support) to the Team plan.

Taxes and fees apply as stated in the offer you receive at checkout or in your quote.

Start or migrate the workload

Request a Figment Mac and run the Phase 2 checkpoint script before you touch an update — that way your first rollback target exists on day one, not after the first incident.

Start a hosted Mac at Figment → · Browse Mac hosting plans →

Related guides:


Subscribe for Figma design and website publishing guides.


Figment.so

Contact

Twitter

Website pricing

Compute index

About

Privacy

Terms