# Disk snapshots
URL: /docs/ios/snapshots
LLM index: /llms.txt
Description: Reuse build state across Xcode instances with disk snapshots and branch fallbacks.

# 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](/docs/quickstart), run these commands from your project directory. Replace `MyApp` with your scheme:

```bash
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:

```bash
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:

```bash
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

- Keep the synced project folder's name the same between runs. A different folder name puts the source at a different remote path, so Xcode cannot reuse the previous build state.
- Changing Xcode versions clears incompatible cached build state. Use a separate snapshot key for each version.
- A restore must use the same snapshot path set as the saved archive. Use a new key if you change that set.
- A failed compile or cancelled build does not produce a new snapshot. A test build can still save its snapshot if compilation succeeds but tests fail. Syncing changes after a successful build prevents saving until another build succeeds. Running commands with `lim xcode run` alone does not qualify a workspace for publication.
- A concurrent instance using the same restored workspace on the same host can cause a cold fallback with saving disabled. The CLI reports the restore outcome; check the `--wait-snapshot` output for the save outcome.

## 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:

```ts
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.