Skip to content

What macbatch is ​

macbatch runs latency-tolerant AI batch work across Macs you already own.

The unit of work is a job: a list of items and a model. The control plane packs those items into shards, workers lease shards, run the model locally through Ollama, and post results back. If a worker disappears mid-shard, its lease expires and another worker picks the shard up.

for item in items:
    result = f(model, item)     # one whole model, one worker, one shard

Every worker runs a complete model. Nothing is split across machines — no tensor parallelism, no layer sharding, no low-latency requirement between nodes. That is what makes a friend's Mac on a different continent a usable worker.

When this is the right tool ​

Good fitBad fit
Re-embedding a corpus after switching modelsChat completions behind a user-facing request
OCR over a backlog of scanned pagesAnything with a per-request latency SLA
Nightly enrichment or classification passesTraining or fine-tuning
Work measured in hours, not millisecondsJobs needing one model larger than a single Mac's memory

If a job can wait an hour, macbatch can probably run it. If it cannot wait a second, macbatch is the wrong shape entirely.

The three processes ​

Control plane — one FastAPI process holding a SQLite queue. It hands out leases and records results. It never calls workers.

Worker — macbatch serve, a loop that heartbeats, leases shards, runs them, and bulk-posts completions. Runs on any Mac with an outbound internet connection.

Ollama — the actual runtime, listening on 127.0.0.1:11434 on each worker. macbatch does not implement inference; it schedules it.

Job to resultmap-reduce shape of a single job
1 · items500 items · 1 cell ≈ 5shard_size= 322 · shards16 tasks, one row in `tasks`lease3 · workerspull up to lease_limitworker 1 embed shard locally worker 2 embed shard locally worker 3 embed shard locally complete_batch4 · resultsshards gathered, then flattened results[] — one entry per shard task_id, worker_id, duration_ms items[] — flat, 500 embeddings this is what consumers read GET /v1/jobs/{'{'}job_id{'}'}/results
shard_size is the main tuning knob. It trades control-plane chatter against reassignment granularity — a bigger shard means fewer HTTP round trips but more lost work when a worker dies mid-shard.

Failure is the default assumption ​

Consumer laptops close their lids, lose Wi-Fi, and get carried out of the house. The scheduler treats every worker as temporary:

  • Delivery is at-least-once. A task can run twice; make your consumers idempotent.
  • Leases expire after 600 seconds and the task returns to pending.
  • A task that errors is requeued until it has been attempted three times, then marked failed.
  • A job finishes as completed if nothing failed, partial if something did.

See Leases and failure for the exact state machine.

Next ​

MIT licensed. Every benchmark on this site is reproducible with macbatch bench.