> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tokenfactory.nebius.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Prepare once, reuse safely

> Save one prepared filesystem image and reuse it in independent later operations.

Use one retained setup operation when several tasks need the same dependencies or fixtures. Each task starts on new compute from the saved filesystem image; processes, memory, services, and connections from setup are gone.

## Prerequisites

* Complete [Set up access](/sandboxes/start/set-up-access).
* In a fresh project, run `uv init --python 3.12` and `uv add "contree-sdk==0.3.6"`.
* Use an immutable image UUID already in the project, or allow the example to import `docker.io/library/alpine:3.19`.

## Complete source

Save as `reuse.py`:

```python theme={null}
import asyncio
from uuid import UUID

from contree_sdk import Contree


def require_success(run, label: str) -> None:
    if run.exit_code != 0:
        raise RuntimeError(f"{label} failed: {run.stderr}")


async def main() -> None:
    client = Contree()
    base = await client.images.oci("docker.io/library/alpine:3.19")
    if not isinstance(base.uuid, UUID):
        raise RuntimeError('Image import did not return a UUID')

    prepared = await base.run(
        shell="mkdir -p /workspace && printf 'prepared-v1\\n' > /workspace/prepared.txt",
        disposable=False,
        timeout=60,
    )
    require_success(prepared, "prepare")
    if prepared.uuid is None:
        raise RuntimeError("Preparation returned no reusable image")
    checkpoint_uuid = prepared.uuid

    checkpoint = await client.images.use(checkpoint_uuid, strict=True)
    first, second = await asyncio.gather(
        checkpoint.run(
            shell="test \"$(cat /workspace/prepared.txt)\" = prepared-v1 && printf 'A\\n'",
            disposable=True,
            timeout=30,
        ),
        checkpoint.run(
            shell="test \"$(cat /workspace/prepared.txt)\" = prepared-v1 && printf 'B\\n'",
            disposable=True,
            timeout=30,
        ),
    )
    require_success(first, "task A")
    require_success(second, "task B")
    if first.stdout != "A\n":
        raise RuntimeError('Task A did not return its expected output')
    if second.stdout != "B\n":
        raise RuntimeError('Task B did not return its expected output')
    if first.uuid is not None or second.uuid is not None:
        raise RuntimeError('Disposable tasks unexpectedly returned saved images')
    print(checkpoint_uuid)
    print(first.stdout + second.stdout, end="")


if __name__ == "__main__":
    asyncio.run(main())
```

Run `uv run python reuse.py`. The output ends with `A` and `B`. The first line is the immutable checkpoint UUID to record with the setup revision.

## State, failure, and retention

The setup explicitly uses `disposable=False`; its UUID is the branch source. Both readers explicitly use `disposable=True`, so they return output but no reusable image. SDK 0.3.6 represents that outcome with `uuid is None`; attempting another run from that result raises `DisposableImageRunError` locally.

If either reader fails, inspect its stdout, stderr, exit code, and `first.result.truncated` or `second.result.truncated`, then rerun only that task from `checkpoint_uuid`. An SDK wait timeout triggers a best-effort cancellation; do not resubmit until the operation outcome is known. Checkpoint images are retained for 180 days; after that, untagged and unreferenced images may be deleted. See [Limits, retention, and usage](/sandboxes/operate/limits-retention-and-usage) before choosing a long-term storage strategy.

## References

* [Python SDK image reference](/sandboxes/sdk/python_sdk/reference/image)
* [Python branching guide](/sandboxes/sdk/python_sdk/branching)
* [Limits, retention, and usage](/sandboxes/operate/limits-retention-and-usage)
