Xcode Builds

Compile iOS without owning a Mac.

Xcode builds and Swift test suites on dedicated Apple-silicon Macs we rack ourselves. One tenant per VM, destroyed when the build exits. Archive, sign, export to TestFlight, billed per minute, with source sync and cache restore free.

curl -fsSL https://sandal.dev/install | sh
terminal
$ sandal xcode build . --scheme MyApp --configuration Release
sync 6 of 214 chunks · 11.4 MB · 0.9s
cache DerivedData + SPM restored · myapp-main
host m4-eu-3 · Virtualization.framework · fresh VM
build Compiling Swift · 412 files
ok MyApp.ipa · 41.2 MB · 3m 06s
artifacts ipa dSYM xcresult logs
$0.01per build minute
Apple silicondedicated
1 tenantper build VM
per secondmetering
14 daysartifact retention
Xcode Builds

Your source in, an artifact out.

Each build runs inside its own ephemeral VM through Virtualization.framework. You do not get a shell on somebody’s Mac mini, you get a clean machine, your source, and an artifact.

Source sync

Only the chunks that changed.

Files are split into content-addressed chunks and hashed. The client uploads only the hashes the server is missing, so a second build ships kilobytes, not the repository. .git, Pods, DerivedData and Carthage are skipped by default; source sync is not billed.

uploaded this build already on the server ignored path. A typical second build moves 6 of 214 chunks.
The build API

Logs stream as xcodebuild writes them.

destination takes exactly what you would pass to xcodebuild -destination. Output is unbuffered, so a slow compile shows progress. The meter runs from the moment xcodebuild starts to the moment it exits, not source sync, cache restore or queue time. Point --webhook-url at your endpoint to run it async.

const build = await sandal.builds.xcode.run({
source: "./MyApp",
scheme: "MyApp",
configuration: "Release",
destination: "generic/platform=iOS",
signing: { grant: "sign_8f2c1a" },
cache: { namespace: "myapp-main" },
});
for await (const line of build.logs()) process.stdout.write(line);
Signing & caches

Certificates never travel with a build.

Register signing material once as a grant; the build request carries only grant.id, and the certificate is decrypted into the build VM’s keychain at build time and destroyed with the VM. DerivedData, SPM and CocoaPods each get a cache volume keyed by the resolved dependency graph, a matching digest mounts before the build starts.

Incremental Swift build of a 340k-line app: 9m 12s → 2m 38s with a warm cache.

const grant = await sandal.signing.grants.create({
name: "myapp-appstore",
certificate: fs.readFileSync("./dist.p12"),
password: process.env.CERT_PASSWORD,
profiles: [fs.readFileSync("./AppStore.mobileprovision")],
});
await sandal.builds.xcode.run({
source: "./MyApp",
scheme: "MyApp",
signing: { grant: grant.id }, // an id, not a secret
});
Build → device

The artifact installs itself.

Pass attachTo and the finished .app is installed and launched on a live simulator session, no download, no shell in between. Write Swift, build, watch it run, read the screen, iterate.

Drive the result on a simulator
const dev = await sandal.devices.create({ kind: "ios-simulator" });
const build = await sandal.builds.xcode.run({
source: "./MyApp", scheme: "MyApp", attachTo: dev.id,
});
await dev.tap(120, 640); // already installed & launched
Artifacts
.appSimulator and device builds
.ipaArchive builds with signing configured
.xcarchivexcodebuild archive
.xcresultEvery build, tests, warnings, timing
dSYMRelease builds with debug symbols
logsAlways, the raw xcodebuild output

Behind 15-minute signed links, or written straight to your own S3, GCS or R2 bucket. Kept 14 days.

Speed

Faster because
the hardware is faster.

Apple silicon we own, one job at a time, with sticky DerivedData across builds. Medians on public iOS repositories.

Workload Test Comparison

2xFaster
iOS archive
7m 18s
14m 50s
3.5xFaster
Incremental Swift build
2m 38s
9m 12s
2xFaster
XCUITest suite
5m 05s
10m 10s
Sandal GitHub macOS
Why Sandal

Named hardware, no markup.

Workload
GitHub macOS
Sandal
iOS archivexcodebuild archive, Xcode 16.4
14m 50s
7m 18s
Incremental Swift build340k-line app, warm DerivedData cache
9m 12s
2m 38s
Price per build minuteM-series, 4 cores / 8 GB
$0.062
$0.01

Xcode 16.4, 16.2 and 15.4 on the image, selectable with xcode-select. iOS 17 and 18 simulator runtimes ship with it. Disk is 150 GB.

Owned Macs

No hosted-Mac markup.

We buy and rack Apple silicon rather than renting EC2 Macs or reselling GitHub’s pool. That is why a minute is $0.01 against GitHub’s $0.062 and Depot’s $0.08.

Ephemeral

A clean keychain every job.

Each job gets a fresh VM from a golden image through Virtualization.framework, deleted at the end. Provisioning profiles and derived data never cross between builds.

Preinstalled

Xcode and the toolchain, ready.

Homebrew, fastlane, CocoaPods and a warm SwiftPM cache are on the image. Warm minis claim in 12 seconds instead of waiting on a full boot and login.

Plans

One balance. Every machine you run.

Xcode builds draw from the same credit pool as your sandboxes, runners and simulators, billed by the second at one published rate card.

Starter

For a solo build or a first agent in production.

$20/ month

$20 of usage credits

≈ 550 VM-hours or 33,000 CI minutes

Get started
  • 20 concurrent machines
  • Committed in-plan rates on every product
  • Linux & Windows, sandboxes, VMs, runners
  • Per-second billing, paused is free
  • Full API, SDKs, CLI & MCP server
  • Unlimited seats
  • Community support
Most popular

Team

Apple silicon unlocked, and a balance that rolls over.

$100/ month

$100 of usage credits

≈ 2,700 VM-hours or 10,000 Mac build minutes

Get started
  • 100 concurrent machines
  • Everything in Starter, plus:
  • macOS, iOS/Android simulators & Xcode builds
  • Unused credits roll over one month
  • Usage exports & per-second line items
  • Email support

Business

Higher ceilings for a team leaning on the fleet daily.

$250/ month

$250 of usage credits

≈ 6,900 VM-hours or 25,000 Mac build minutes

Get started
  • 300 concurrent machines
  • Everything in Team, plus:
  • Raised concurrency & start rate
  • Priority support & shared Slack channel
  • Single sign-on (SAML / OIDC)
  • Volume credit rate

Enterprise

Dedicated capacity, your compliance surface, one invoice.

Custom

Committed volume rate

Committed volume rate, negotiated per account

Talk to sales
  • Reserved bare-metal capacity
  • BYOC or self-host the control plane
  • SSO & SCIM, audit logs, data residency
  • Annual invoicing, MSA, DPA
  • Support SLA & solutions engineering
  • Custom regions on request

Every plan spends its credit pool first, then usage keeps flowing and bills per second at your committed in-plan rate, no hard stop mid-run, no forced upgrade. Without a plan you pay the on-demand rate instead. Prices are illustrative pending final list pricing; the per-second billing model is final.

Rate card

The Xcode build rate card.

Apple silicon

MachineIn-plan / minuteOn-demand / minute
M-series · 4 cores / 8 GB$0.01$0.015
M-series Pro · 8 cores / 16 GB$0.02$0.03
M-series Max · 12 cores / 32 GB$0.04$0.06

Dedicated Apple-silicon build machines. Larger instances scale on the same per-minute ladder. On-demand is 1.5× the in-plan rate. All usage bills by the second; paused machines are not billed.

Get started

Give your AI agents somewhere to run today.

Your agents get their own machines across Linux, macOS, Windows, iOS and Xcode in milliseconds, and you stop paying for idle.

npx sandal init