Llim.run

Disk snapshots

Use a disk snapshot to carry source files, dependencies, and Xcode's DerivedData into your next instance. Snapshots belong to your organization. Without snapshot configuration, a new instance starts without a saved workspace.

Save and reuse a disk snapshot

With the CLI installed and LIM_API_KEY set, run these commands from your project directory. Replace MyApp with your scheme:

XCODE_ID=$(lim xcode create --snapshot-key myapp-main --quiet)
lim xcode build . --id "$XCODE_ID" --scheme MyApp
lim xcode delete "$XCODE_ID" --wait-snapshot

The first run builds from scratch. After a successful build, deleting the instance saves its workspace under myapp-main. Run the same sequence again to restore that snapshot before building. Each successful publication replaces the previous archive under that key.

--wait-snapshot waits for publication and prints its result. Without it, deletion returns while publication continues in the background. Wait for publication before starting a build that needs the new snapshot. In CI, put the delete command in a cleanup step that runs even if the build fails.

Leave --snapshot-paths unset to save the whole workspace. This keeps source files and their sync state alongside DerivedData so Xcode can reuse unchanged build outputs. --basis-cache-dir is a separate, local cache for source uploads; it does not preserve remote build state.

You can keep building on the same running instance without restoring again. The saved snapshot is for future instances and is only published when this instance terminates.

Restore from another branch

Use --snapshot-key for the destination and --snapshot-restore-keys for an ordered list of sources. For example, restore a pull request's previous snapshot, falling back to main, then save under the pull request's key:

XCODE_ID=$(lim xcode create \
  --snapshot-key myapp-pr51 \
  --snapshot-restore-keys "myapp-pr51,myapp-main" \
  --quiet)
lim xcode build . --id "$XCODE_ID" --scheme MyApp
lim xcode delete "$XCODE_ID" --wait-snapshot

For each restore key, Limrun tries an exact match, then the most recently published snapshot whose key starts with that prefix. It moves to the next restore key only if neither matches. Prefixes are literal; no * is needed. With no match, the instance starts cold and can save a new snapshot after building.

When --snapshot-restore-keys is omitted, --snapshot-key is also the restore key. When you supply a restore list, include the destination key yourself if you want to try it first.

To consume a snapshot without replacing it, create an instance with only restore keys:

lim xcode create --snapshot-restore-keys myapp-main

Build on that instance as usual and delete it when finished. It will not publish a snapshot unless you explicitly bind a destination key later.

Use keys that identify the project and Xcode version, such as myapp-xcode27-main. Keep fallbacks within the same Xcode version. For concurrent CI jobs, use separate destination keys to avoid overwriting each other's results.

Configure disk snapshots before the build

Set snapshot options on lim xcode create for predictable behavior. Restore keys and snapshot paths are fixed when the instance is created. --reuse-if-exists keeps the reused instance's original configuration.

lim xcode build --snapshot-key ... also works when the command creates a new instance. On an existing instance, it only binds a save destination to a workspace already prepared for snapshots. It does not restore a snapshot or enable snapshots on an instance created without snapshots. Once a destination key is set, it cannot be changed for that instance.

Understand a cold build or skipped save

Use the TypeScript SDK

Pass the same settings as spec.snapshot. Wait for the restore before syncing, build, then delete the instance to trigger publication:

import Limrun from '@limrun/api';

const lim = new Limrun({ apiKey: process.env['LIM_API_KEY'] });
const instance = await lim.xcodeInstances.create({
  wait: true,
  spec: {
    snapshot: {
      key: 'myapp-pr51',
      restoreKeys: ['myapp-pr51', 'myapp-main'],
    },
  },
});

try {
  const { snapshot, gone } = await lim.xcodeInstances.followSnapshot(instance.metadata.id);
  if (gone || snapshot.restore.phase === 'failed') {
    throw new Error(snapshot.restore.message ?? 'Disk snapshot restore did not complete');
  }

  const xcode = await lim.xcodeInstances.createClient({ instance });
  await xcode.sync('./my-app');
  const result = await xcode.xcodebuild({ scheme: 'MyApp' });
  if (result.exitCode !== 0) throw new Error(`Build ${result.status}`);
} finally {
  await lim.xcodeInstances.delete(instance.metadata.id);
}

SDK deletion returns before publication finishes. To track progress, use getSnapshot(id) for the current state or followSnapshot(id, { side: 'save' }) for updates. Start the save watcher and wait for its onOpen callback before deleting, so it can observe publication before the instance disappears. A save phase of published confirms success; skipped means nothing was saved.

Existing --cache-* flags, --wait-cache, spec.cache, and cache-named SDK methods remain supported for compatibility. Use the snapshot names in new integrations.