---
title: Images
description: Python SDK - Image cache API reference
---

Configure sandbox image sources and manage the local OCI image cache.

## Image

Factory for sandbox image sources.

#### <span className="msb-recv">Image.</span><span className="msb-hn">oci()</span>

```python
@staticmethod
def oci(
    reference: str,
    *,
    root_disk: RootDiskConfig | int | None = None,
    upper_size_mib: int | None = None,
) -> ImageSource
```

<Accordion title="Example">

```python
from microsandbox import Image, RootDisk, Sandbox

sb = await Sandbox.create(
    "api",
    image=Image.oci("python:3.12", root_disk=RootDisk.managed(8192)),
)
```

</Accordion>

Create an OCI image rootfs source. Use `root_disk` to configure its writable layer with a [`RootDisk`](#rootdisk) factory result. An integer is shorthand for a managed disk of that size in MiB.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>reference</code><span className="msb-type">str</span></div>
    <div className="msb-param-desc">OCI image reference, e.g. <code>"python:3.12"</code>.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>root_disk</code><span className="msb-type">RootDiskConfig | int | None</span></div>
    <div className="msb-param-desc">Writable root disk configuration or managed-disk size in MiB.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>upper_size_mib</code><span className="msb-type">int | None</span></div>
    <div className="msb-param-desc">Deprecated managed-disk size alias. Use <code>root_disk=RootDisk.managed(...)</code>.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#imagesource">ImageSource</a></div>
    <div className="msb-param-desc">Rootfs source for <code>image=</code>.</div>
  </div>
</div>

#### <span className="msb-recv">Image.</span><span className="msb-hn">bind()</span>

```python
@staticmethod
def bind(path: str) -> ImageSource
```

<Accordion title="Example">

```python
sb = await Sandbox.create("api", image=Image.bind("/srv/rootfs"))
```

</Accordion>

Create a rootfs source that binds a host directory as the guest root filesystem.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>path</code><span className="msb-type">str</span></div>
    <div className="msb-param-desc">Host directory to use as the rootfs.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#imagesource">ImageSource</a></div>
    <div className="msb-param-desc">Rootfs source for <code>image=</code>.</div>
  </div>
</div>

#### <span className="msb-recv">Image.</span><span className="msb-hn">disk()</span>

```python
@staticmethod
def disk(path: str, *, fstype: str | None = None) -> ImageSource
```

<Accordion title="Example">

```python
sb = await Sandbox.create(
    "api",
    image=Image.disk("/data/root.qcow2", fstype="ext4"),
)
```

</Accordion>

Create a rootfs source backed by a disk image. The format is inferred from the file extension. Pass `fstype` when the filesystem type cannot be auto-detected.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>path</code><span className="msb-type">str</span></div>
    <div className="msb-param-desc">Path to the disk image (e.g. <code>.qcow2</code>, <code>.raw</code>, <code>.vmdk</code>).</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>fstype</code><span className="msb-type">str | None</span></div>
    <div className="msb-param-desc">Filesystem type, e.g. <code>"ext4"</code>. <code>None</code> auto-detects.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#imagesource">ImageSource</a></div>
    <div className="msb-param-desc">Rootfs source for <code>image=</code>.</div>
  </div>
</div>

<p className="msb-member-group">Cache management</p>

These static methods inspect and prune images already pulled into the local OCI cache. They require a local backend; on a cloud backend they raise `UnsupportedError`.

#### <span className="msb-recv">Image.</span><span className="msb-hn">get()</span>

```python
@staticmethod
async def get(reference: str) -> ImageHandle
```

<Accordion title="Example">

```python
handle = await Image.get("python:3.12")
print(handle.reference, handle.layer_count)
```

</Accordion>

Fetch one cached image by reference. Raises [`ImageNotFoundError`](#errors) when the image is not present in the local cache.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>reference</code><span className="msb-type">str</span></div>
    <div className="msb-param-desc">Image reference to look up.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#imagehandle">ImageHandle</a></div>
    <div className="msb-param-desc">Handle to the cached image.</div>
  </div>
</div>

#### <span className="msb-recv">Image.</span><span className="msb-hn">list()</span>

```python
@staticmethod
async def list() -> list[ImageHandle]
```

<Accordion title="Example">

```python
for image in await Image.list():
    print(image.reference, image.size_bytes)
```

</Accordion>

Return every cached image.

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#imagehandle">list[ImageHandle]</a></div>
    <div className="msb-param-desc">All cached image handles.</div>
  </div>
</div>

#### <span className="msb-recv">Image.</span><span className="msb-hn">inspect()</span>

```python
@staticmethod
async def inspect(reference: str) -> ImageDetail
```

<Accordion title="Example">

```python
detail = await Image.inspect("python:3.12")
print(detail.handle.reference)
for layer in detail.layers:
    print(layer.position, layer.diff_id)
```

</Accordion>

Return handle metadata plus the parsed OCI config and per-layer detail.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>reference</code><span className="msb-type">str</span></div>
    <div className="msb-param-desc">Image reference to inspect.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#imagedetail">ImageDetail</a></div>
    <div className="msb-param-desc">Handle, OCI config, and layers.</div>
  </div>
</div>

#### <span className="msb-recv">Image.</span><span className="msb-hn">remove()</span>

```python
@staticmethod
async def remove(reference: str, *, force: bool = False) -> None
```

<Accordion title="Example">

```python
await Image.remove("python:3.12", force=True)
```

</Accordion>

Delete a cached image. When `force` is `False`, an image still referenced by one or more sandboxes raises [`ImageInUseError`](#errors); pass `force=True` to remove it anyway.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>reference</code><span className="msb-type">str</span></div>
    <div className="msb-param-desc">Image reference to delete.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>force</code><span className="msb-type">bool</span></div>
    <div className="msb-param-desc">Remove even if still referenced. Default <code>False</code>.</div>
  </div>
</div>

#### <span className="msb-recv">Image.</span><span className="msb-hn">prune()</span>

```python
@staticmethod
async def prune() -> ImagePruneReport
```

<Accordion title="Example">

```python
report = await Image.prune()
print(f"{report.layers_removed} layers, {report.bytes_reclaimed} bytes")
```

</Accordion>

Remove cached image data that is not used by any sandbox or indexed snapshot. The returned report counts the removed refs, manifests, layers, fsmeta files, and VMDK files, plus any measured bytes reclaimed.

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#imageprunereport">ImagePruneReport</a></div>
    <div className="msb-param-desc">Counts of removed data and bytes reclaimed.</div>
  </div>
</div>

<Accordion title="Example">

```python
report = await Image.prune()
print(f"{report.layers_removed} layers, {report.bytes_reclaimed} bytes")
```

</Accordion>

---

#### <span className="msb-recv">Image.</span><span className="msb-hn">load()</span>
<div className="msb-tags"><span className="msb-tag is-static">static</span><span className="msb-tag is-async">async</span></div>

```python
@staticmethod
async def load(input_path: str, *, tag: str | None = None) -> list[ImageHandle]
```

Import images from a local archive into the cache. Accepts `docker save` tarballs and OCI Image Layout archives, so locally built images can be used without going through a registry.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>input_path</code><span className="msb-type">str</span></div>
    <div className="msb-param-desc">Path to the archive file, or <code>"-"</code> to read the archive from stdin.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>tag</code><span className="msb-type">str | None</span></div>
    <div className="msb-param-desc">Extra reference applied to the first image in the archive.</div>
  </div>
</div>

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#imagehandle">list[ImageHandle]</a></div>
    <div className="msb-param-desc">A handle for every image reference imported.</div>
  </div>
</div>

<Accordion title="Example">

```python
# docker save my-image:latest -o my-image.tar
images = await Image.load(input_path="my-image.tar", tag="app:local")
for image in images:
    print(image.reference, image.layer_count)

# Or pipe it in: docker save my-image:latest | python app.py
images = await Image.load(input_path="-")
```

</Accordion>

---

#### <span className="msb-recv">Image.</span><span className="msb-hn">save()</span>
<div className="msb-tags"><span className="msb-tag is-static">static</span><span className="msb-tag is-async">async</span></div>

```python
@staticmethod
async def save(
    reference: str | Sequence[str],
    *,
    output_path: str,
    format: ImageArchiveFormat = ImageArchiveFormat.DOCKER,
) -> None
```

Export one or more cached images to an archive file. Raises [`ImageNotFoundError`](#errors) when any reference is not in the local cache, and `ValueError` for an empty reference list.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>reference</code><span className="msb-type">str | Sequence[str]</span></div>
    <div className="msb-param-desc">Cached image reference to export, or a sequence of references written into the same archive.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>output_path</code><span className="msb-type">str</span></div>
    <div className="msb-param-desc">File path to write the archive to.</div>
  </div>
  <div className="msb-param">
    <div className="msb-param-key"><code>format</code><a className="msb-type" href="#imagearchiveformat">ImageArchiveFormat</a></div>
    <div className="msb-param-desc">Archive layout. Defaults to <code>DOCKER</code>.</div>
  </div>
</div>

<Accordion title="Example">

```python
from microsandbox import ImageArchiveFormat

await Image.save("python:3.12", output_path="python.tar")
await Image.save(
    "python:3.12",
    output_path="python-oci.tar",
    format=ImageArchiveFormat.OCI,
)
await Image.save(["python:3.12", "app:local"], output_path="bundle.tar")
```

</Accordion>

## ImageHandle


<div className="msb-tags"><span className="msb-tag is-type">class</span></div>

<p className="msb-backref">Returned by <a href="#image-get">get()</a> · <a href="#image-list">list()</a> · <a href="#image-load">load()</a></p>

A lightweight handle to a cached OCI image, returned by [`Image.get()`](#image-get), [`Image.list()`](#image-list), and [`Image.load()`](#image-load). Properties are read-only attributes; the two methods are async.

#### <span className="msb-recv">handle.</span><span className="msb-hn">reference</span>

`str`

Image reference

#### <span className="msb-recv">handle.</span><span className="msb-hn">size_bytes</span>

`int \| None`

Total size in bytes, or `None` when unknown

#### <span className="msb-recv">handle.</span><span className="msb-hn">manifest_digest</span>

`str \| None`

Content-addressable manifest digest

#### <span className="msb-recv">handle.</span><span className="msb-hn">architecture</span>

`str \| None`

Resolved architecture

#### <span className="msb-recv">handle.</span><span className="msb-hn">os</span>

`str \| None`

Resolved operating system

#### <span className="msb-recv">handle.</span><span className="msb-hn">layer_count</span>

`int`

Number of layers

#### <span className="msb-recv">handle.</span><span className="msb-hn">last_used_at</span>

`float \| None`

Last referenced time, milliseconds since epoch

#### <span className="msb-recv">handle.</span><span className="msb-hn">created_at</span>

`float \| None`

First-pulled time, milliseconds since epoch

#### <span className="msb-recv">handle.</span><span className="msb-hn">inspect()</span>

```python
await inspect()
```

Fetch full detail for this image

<p className="msb-label">Returns</p>

[`ImageDetail`](#imagedetail)

#### <span className="msb-recv">handle.</span><span className="msb-hn">remove()</span>

```python
await remove(*, force=False)
```

Delete this image (raises [`ImageInUseError`](#errors) unless `force`)

## RootDisk

Factory for writable OCI root disk configurations.

```python
from microsandbox import DiskImageFormat, RootDisk

managed = RootDisk.managed(8192)
temporary = RootDisk.tmpfs(512)
existing = RootDisk.disk(
    "/data/upper.raw",
    format=DiskImageFormat.RAW,
    fstype="ext4",
)
```


#### <span className="msb-recv">RootDisk.</span><span className="msb-hn">managed()</span>

```python
managed(size_mib=None)
```

Microsandbox-managed sparse ext4 disk

<p className="msb-label">Returns</p>

[`RootDiskConfig`](#rootdiskconfig)

#### <span className="msb-recv">RootDisk.</span><span className="msb-hn">tmpfs()</span>

```python
tmpfs(size_mib=None)
```

Ephemeral RAM-backed upper layer

<p className="msb-label">Returns</p>

[`RootDiskConfig`](#rootdiskconfig)

#### <span className="msb-recv">RootDisk.</span><span className="msb-hn">disk()</span>

```python
disk(path, *, format=None, fstype=None)
```

User-supplied writable disk image

<p className="msb-label">Returns</p>

[`RootDiskConfig`](#rootdiskconfig)

## Types

### ImageSource

<p className="msb-backref">Returned by <a href="#image-oci">oci()</a> · <a href="#image-bind">bind()</a> · <a href="#image-disk">disk()</a></p>

Explicit rootfs image source. Build one with [`Image.oci()`](#image-oci), [`Image.bind()`](#image-bind), or [`Image.disk()`](#image-disk), then pass it as the `image=` kwarg to [`Sandbox.create()`](/sdk/python/sandbox). A frozen dataclass; treat its fields as opaque.

| Field | Type | Description |
|-------|------|-------------|
| `_type` | [`ImageSourceKind`](#imagesourcekind) | Source kind |
| `_path` | `str \| None` | Host path for `bind` / `disk` sources |
| `_reference` | `str \| None` | OCI reference for `oci` sources |
| `_upper_size_mib` | `int \| None` | Writable overlay upper size in MiB (OCI only) |
| `_fstype` | `str \| None` | Filesystem type for `disk` sources |
| `_format` | [`DiskImageFormat`](#diskimageformat)` \| None` | Disk image format (inferred from extension) |

### RootDiskConfig

Frozen root disk configuration produced by [`RootDisk`](#rootdisk).

| Field | Type | Description |
|-------|------|-------------|
| `kind` | [`RootDiskKind`](#rootdiskkind) | Root disk implementation |
| `size_mib` | `int \| None` | Managed disk or tmpfs size |
| `path` | `str \| None` | User-supplied disk image path |
| `format` | [`DiskImageFormat`](#diskimageformat)` \| None` | Disk image format |
| `fstype` | `str \| None` | Inner filesystem type |

### ImageDetail

<p className="msb-backref">Returned by <a href="#image-inspect">inspect()</a> · <a href="#imagehandle">ImageHandle.inspect()</a></p>

Full detail for a cached image: the core handle, the parsed OCI config block, and per-layer metadata.

| Property | Type | Description |
|----------|------|-------------|
| `handle` | [`ImageHandle`](#imagehandle) | Core cached image metadata |
| `config` | [`ImageConfigDetail`](#imageconfigdetail)` \| None` | Parsed OCI config block |
| `layers` | `list[`[`ImageLayerDetail`](#imagelayerdetail)`]` | Layers in bottom-to-top order |

### ImageConfigDetail

<p className="msb-backref">Used by <a href="#imagedetail">ImageDetail.config</a></p>

OCI image config fields extracted from the local cache.

| Property | Type | Description |
|----------|------|-------------|
| `digest` | `str` | Config blob digest |
| `env` | `list[str]` | Environment variables (`KEY=value`) |
| `cmd` | `list[str] \| None` | Default command |
| `entrypoint` | `list[str] \| None` | Image entrypoint |
| `working_dir` | `str \| None` | Default working directory |
| `user` | `str \| None` | Default user |
| `labels` | `dict[str, Any] \| None` | OCI labels |
| `stop_signal` | `str \| None` | Configured stop signal |

### ImageLayerDetail

<p className="msb-backref">Used by <a href="#imagedetail">ImageDetail.layers</a></p>

Metadata for a single image layer.

| Property | Type | Description |
|----------|------|-------------|
| `diff_id` | `str` | Uncompressed layer diff id |
| `blob_digest` | `str` | Compressed blob digest |
| `media_type` | `str \| None` | Layer media type |
| `compressed_size_bytes` | `int \| None` | Compressed size in bytes |
| `erofs_size_bytes` | `int \| None` | Size of the generated EROFS sidecar in bytes |
| `position` | `int` | Layer position (bottom to top) |

### ImagePruneReport

<p className="msb-backref">Returned by <a href="#image-prune">prune()</a></p>

Summary of cached image data removed by [`Image.prune()`](#image-prune).

| Property | Type | Description |
|----------|------|-------------|
| `image_refs_removed` | `int` | Number of image refs removed |
| `manifests_removed` | `int` | Number of manifests removed |
| `layers_removed` | `int` | Number of layer blobs removed |
| `fsmeta_removed` | `int` | Number of fsmeta sidecar files removed |
| `vmdk_removed` | `int` | Number of VMDK files removed |
| `bytes_reclaimed` | `int \| None` | Measured bytes reclaimed, or `None` when not measured |

### DiskImageFormat

<p className="msb-backref">Used by <a href="#imagesource">ImageSource._format</a></p>

Disk image container format.

| Member | Value | Description |
|--------|-------|-------------|
| `DiskImageFormat.QCOW2` | `"qcow2"` | QEMU copy-on-write v2 |
| `DiskImageFormat.RAW` | `"raw"` | Raw block image |
| `DiskImageFormat.VMDK` | `"vmdk"` | VMware disk image |

### ImageArchiveFormat

<p className="msb-backref">Used by <a href="#image-save">Image.save(format=...)</a></p>

Archive layout used when exporting cached images.

| Member | Value | Description |
|--------|-------|-------------|
| `ImageArchiveFormat.DOCKER` | `"docker"` | Docker archive compatible with `docker load` |
| `ImageArchiveFormat.OCI` | `"oci"` | OCI Image Layout archive |

### ImageSourceKind

<p className="msb-backref">Returned in <a href="#imagesource">ImageSource._type</a></p>

Root filesystem source kind.

| Member | Value | Description |
|--------|-------|-------------|
| `ImageSourceKind.OCI` | `"oci"` | OCI image reference |
| `ImageSourceKind.BIND` | `"bind"` | Host directory bind source |
| `ImageSourceKind.DISK` | `"disk"` | Host disk-image source |

### RootDiskKind

<p className="msb-backref">Returned in <a href="#rootdiskconfig">RootDiskConfig.kind</a></p>

Writable OCI root disk implementation.

| Member | Value | Description |
|--------|-------|-------------|
| `RootDiskKind.MANAGED` | `"managed"` | Microsandbox-managed sparse ext4 disk |
| `RootDiskKind.TMPFS` | `"tmpfs"` | Ephemeral RAM-backed upper layer |
| `RootDiskKind.DISK_IMAGE` | `"disk-image"` | User-supplied writable disk image |

### Errors

Image operations raise these typed exceptions, all subclasses of `MicrosandboxError`.

| Exception | Raised when |
|-----------|-------------|
| `ImageNotFoundError` | The image reference could not be resolved in the local cache |
| `ImageInUseError` | The image is still referenced by one or more sandboxes (and `force` was not set) |
| `ImagePullFailedError` | An image pull failed |
| `UnsupportedError` | Cache operations were attempted on a backend that lacks a local cache |
