2026 Mac Mini 7×24 Batch Fairness Matrix: launchd ThrottleInterval, renice, and IO-aware lanes for long task completion
When several launchd agents and daemons share one Apple Silicon host, fairness is not a slogan: it is the difference between predictable night batches and tail latency that erodes completion rate for long tasks.
This page gives a decision matrix and parameter checklist for ThrottleInterval, renice wrappers, and IO-aware sequencing so hardware spend stays controllable. See scheduling queue matrix, workflow night batch launchd notes, and APFS waterline FAQ. Purchase stays login-free.
Pain points on a single batch host
- Overlapping exits. Independent StartCalendarInterval blocks can fire the same minute, so two heavy jobs begin together, steal SSD bandwidth, and stretch wall-clock time even when each job is healthy.
- CPU priority without spacing. renice alone does not serialize writers; two low-nice processes still contend on the same volume and inflate latency for interactive or latency-sensitive work.
- Silent starvation. Without ThrottleInterval or staggered calendars, a short fast job can restart immediately after a long job finishes, stealing the quiet tail you expected for compaction or verification steps.
Decision matrix: which lever first
Pick the dominant bottleneck before stacking knobs. Measure CPU utilization, disk latency, and queue depth for your own workload; the matrix is a starting posture, not a law.
| Symptom | Primary lever | Secondary lever | Completion goal |
|---|---|---|---|
| Identical jobs collide on the clock edge | ThrottleInterval plus minute-offset StartCalendarInterval | Split labels per lane | Guarantee minimum quiet gap between successful runs |
| CPU pegged, disk calm | renice or fewer parallel workers inside the script | Longer cadence via StartInterval | Protect foreground latency while batch still advances |
| CPU low, wall-clock grows, writes spike | Reduce concurrent writers and stagger heavy phases | Optional LowPriorityIO on supported agents | Keep APFS metadata and data IO predictable overnight |
Parameter checklist
| Control | Typical starting range | What to watch |
|---|---|---|
ThrottleInterval |
Thirty to three hundred seconds between successful exits for chatty jobs | Still allows overlap if another label launches; pair with offsets |
nice in wrapper |
Five to fifteen for CPU-heavy batch helpers | Starvation if every process is nice; keep one control lane default priority |
Linux-style ionice thinking |
Map to fewer parallel copy or pack threads on macOS hosts | There is no stock ionice; treat the idea as writer concurrency policy |
StartCalendarInterval minute offsets |
Stagger heavy jobs by seven to fifteen minutes inside the same hour | Daylight changes if you assume local wall time; document timezone |
Executable plist shape
Install under each user or global LaunchAgents directory as appropriate; validate with launchctl bootstrap and launchctl print after edits.
<key>Label</key><string>com.example.batch_lane_b</string>
<key>ThrottleInterval</key><integer>120</integer>
<key>StartCalendarInterval</key>
<dict><key>Hour</key><integer>1</integer><key>Minute</key><integer>12</integer></dict>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-c</string>
<string>exec nice -n 10 /usr/local/bin/your-batch --threads=2</string>
</array>
<key>LowPriorityIO</key><true/>
<key>StandardOutPath</key><string>/var/log/your-batch.log</string>
<key>StandardErrorPath</key><string>/var/log/your-batch.err</string>
Night windows and ThrottleInterval
A night window is not only clock time: it is the span where interactive load drops and you can widen throughput safely. Place the longest disk phases first inside that span, then schedule lighter verification jobs after ThrottleInterval gaps so each run sees warm cache and idle queues.
When remote automation fires bursts, align local launchd calendars with those triggers using the same cadence ideas as in the workflow dispatch article so two full scans never start together.
Runbook
- Inventory every periodic launchd label with
StartCalendarIntervalorStartIntervaland log the last five run durations. - Mark jobs as CPU-bound, disk-bound, or mixed; tag writers separately from readers.
- Assign non-overlapping minute buckets within the same hour; add ThrottleInterval only after collisions persist.
- Wrap heavy binaries with
nice; cap internal parallelism before chasing new hardware. - Enable LowPriorityIO only for agents proven safe with your binaries; retest completion time.
- Document rollback: keep previous plist copies and Help Center steps for operators.
FAQ
- Disk contention versus CPU contention on one Mac Mini
- Disk-bound hosts show growing latency with low CPU. CPU-bound hosts saturate cores while disk queues stay short. Mixed hosts need both fewer writers and nicer CPU hogs; never read silence on CPU alone.
- Does ThrottleInterval replace renice
- No. ThrottleInterval spaces successful exits for one label. renice still shapes scheduler precedence among simultaneous processes.
- Can I use ionice on macOS
- Stock Darwin does not ship ionice. Use staggered calendars, writer caps, optional LowPriorityIO, and documented night lanes instead.
Node selection without login friction
Right-size RAM for concurrent mmap-heavy jobs, SSD for log volume plus batch scratch, and core count for the maximum parallel lane you refuse to cap in software. Staying on a cost-controlled tier works when policy enforces spacing and nice discipline.
Citeable gates: ThrottleInterval thirty to three hundred seconds after overlap persists; nice five to fifteen for CPU-heavy helpers; stagger StartCalendarInterval seven to fifteen minutes; map ionice idioms to writer caps on macOS.
Summary. Fair seven by twenty four batches on one Mac Mini combine launchd spacing, honest nice wrappers, and disk-aware night lanes instead of endless hardware churn. Use the matrix, ship plist changes incrementally, then scale through Purchase when policy is exhausted.
Bookmark Home and Blog while you tune calendars; revisit when you add a new nightly exporter.