Back to Microsandbox

Volumes

docs/sdk/python/volumes.mdx

0.6.723.8 KB
Original Source

Create and manage named persistent volumes, and build the mount configs that attach storage to a sandbox. See Volumes for usage examples and patterns.

<p className="msb-label" id="typical-flow">Typical flow</p>
python
from microsandbox import Sandbox, Volume

# 1. create a persistent named volume
await Volume.create("pip-cache", quota_mib=2048)

# 2. mount it into a sandbox via a mount factory
sb = await Sandbox.create(
    "worker",
    image="python",
    volumes={"/root/.cache/pip": Volume.named("pip-cache")},
)

# 3. inspect or edit the volume from the host, no sandbox required
handle = await Volume.get("pip-cache")
print(handle.used_bytes)

Static methods

Static methods on Volume that manage named persistent volumes. Volumes live independently of any sandbox, stored by default under ~/.microsandbox/volumes/<name>/.

<span className="msb-recv">Volume.</span><span className="msb-hn">create()</span>

python
async def create(
    name: str,
    *,
    kind: str = "dir",
    size_mib: int | None = None,
    quota_mib: int | None = None,
    labels: dict[str, str] | None = None,
) -> Volume
<Accordion title="Example">
python
await Volume.create("pip-cache", quota_mib=2048)
await Volume.create("docker-data", kind="disk", size_mib=20 * 1024)
</Accordion>

Create a new named volume. A "dir" volume is a host directory; a "disk" volume is a backing disk image that requires size_mib.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>name</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Volume name.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>kind</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Volume kind: <code>"dir"</code> (default) or <code>"disk"</code>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>size_mib</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">Disk capacity in MiB; required with <code>kind="disk"</code>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>quota_mib</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">Quota in MiB recorded for directory volumes.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>labels</code><span className="msb-type">dict[str, str] | None</span></div> <div className="msb-param-desc">Metadata labels.</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="#instance-properties">Volume</a></div> <div className="msb-param-desc">Created volume, exposing <code>name</code> and <code>path</code>.</div> </div> </div>

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

python
async def get(name: str) -> VolumeHandle
<Accordion title="Example">
python
handle = await Volume.get("pip-cache")
print(handle.kind, handle.used_bytes)
</Accordion>

Get a lightweight handle to an existing named volume, with its metadata and a host-side filesystem handle.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>name</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Volume name.</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="#volumehandle">VolumeHandle</a></div> <div className="msb-param-desc">Handle with metadata and a filesystem accessor.</div> </div> </div>

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

python
async def list() -> list[VolumeHandle]
<Accordion title="Example">
python
for v in await Volume.list():
    print(v.name, v.used_bytes)
</Accordion>

List all named volumes.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#volumehandle">list[VolumeHandle]</a></div> <div className="msb-param-desc">All volume handles.</div> </div> </div>

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

python
async def remove(name: str) -> None
<Accordion title="Example">
python
await Volume.remove("pip-cache")
</Accordion>

Delete a named volume and its contents. Fails if the volume is currently mounted.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>name</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Volume name.</div> </div> </div>

Mount factories

Static factory methods on Volume that build a MountConfig. Pass the result as a value in the volumes dict when creating a sandbox, keyed by the guest mount point.

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

python
def bind(
    path: str,
    *,
    readonly: bool = False,
    noexec: bool = False,
    nosuid: bool = False,
    nodev: bool = False,
) -> MountConfig
<Accordion title="Example">
python
sb = await Sandbox.create(
    "build",
    image="python",
    volumes={"/src": Volume.bind("/home/me/project", readonly=True)},
)
</Accordion>

Mount a host directory into the sandbox. Changes in the guest are reflected on the host and vice versa.

<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">Directory path on the host.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>readonly</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Mount as read-only; virtiofs-backed mounts also reject writes in the host filesystem server.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>noexec</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Prevent direct execution from the mount.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>nosuid</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Ignore setuid and setgid privilege elevation from files on the mount.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>nodev</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Ignore device files on the mount.</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="#mountconfig">MountConfig</a></div> <div className="msb-param-desc">Mount configuration.</div> </div> </div>

<span className="msb-recv">Volume.</span><span className="msb-hn">named()</span>

python
def named(
    name: str,
    *,
    readonly: bool = False,
    noexec: bool = False,
    nosuid: bool = False,
    nodev: bool = False,
) -> MountConfig
<Accordion title="Example">
python
sb = await Sandbox.create(
    "worker",
    image="python",
    volumes={
        "/root/.cache/pip": Volume.named("pip-cache"),
        "/etc/config": Volume.named("shared-config", readonly=True),
    },
)
</Accordion>

Mount an existing named volume. The volume must already exist; create it first with Volume.create().

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>name</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Volume name.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>readonly</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Mount as read-only; virtiofs-backed mounts also reject writes in the host filesystem server.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>noexec</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Prevent direct execution from the mount.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>nosuid</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Ignore setuid and setgid privilege elevation from files on the mount.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>nodev</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Ignore device files on the mount.</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="#mountconfig">MountConfig</a></div> <div className="msb-param-desc">Mount configuration.</div> </div> </div>

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

python
def tmpfs(
    *,
    size_mib: int | None = None,
    readonly: bool = False,
    noexec: bool = False,
    nosuid: bool = False,
    nodev: bool = False,
) -> MountConfig
<Accordion title="Example">
python
sb = await Sandbox.create(
    "scratch",
    image="python",
    volumes={"/tmp/work": Volume.tmpfs(size_mib=256)},
)
</Accordion>

Use an in-memory filesystem. Contents are discarded when the sandbox stops.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>size_mib</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">Maximum size in MiB.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>readonly</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Mount as read-only.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>noexec</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Prevent direct execution from the mount.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>nosuid</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Ignore setuid and setgid privilege elevation from files on the mount.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>nodev</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Ignore device files on the mount.</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="#mountconfig">MountConfig</a></div> <div className="msb-param-desc">Mount configuration.</div> </div> </div>

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

python
def disk(
    path: str,
    *,
    format: str | None = None,
    fstype: str | None = None,
    readonly: bool = False,
    noexec: bool = False,
    nosuid: bool = False,
    nodev: bool = False,
) -> MountConfig
<Accordion title="Example">
python
sb = await Sandbox.create(
    "db",
    image="postgres",
    volumes={"/var/lib/postgresql": Volume.disk("/data/pg.qcow2", fstype="ext4")},
)
</Accordion>

Mount a host disk image as a virtio-blk device. format is the disk image format ("qcow2", "raw", or "vmdk"); when omitted it is inferred from the file extension. fstype (e.g. "ext4") is the inner filesystem agentd mounts; when omitted, agentd probes /proc/filesystems for a type that mounts cleanly.

<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 path to the disk image.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>format</code><span className="msb-type">str | None</span></div> <div className="msb-param-desc">Disk image format hint. See <a href="#diskimageformat">DiskImageFormat</a>.</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">Inner filesystem type.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>readonly</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Mount as read-only.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>noexec</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Prevent direct execution from the mount.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>nosuid</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Ignore setuid and setgid privilege elevation from files on the mount.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>nodev</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Ignore device files on the mount.</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="#mountconfig">MountConfig</a></div> <div className="msb-param-desc">Mount configuration.</div> </div> </div>

Instance properties

Read-only properties on a Volume returned by Volume.create().

<span className="msb-recv">volume.</span><span className="msb-hn">name</span>

python
name: str

Volume name.

<span className="msb-recv">volume.</span><span className="msb-hn">path</span>

python
path: str

Host path to the volume's directory.

VolumeFs methods

Host-side filesystem operations on a named volume, obtained via the fs property on a VolumeHandle. These run directly on the host filesystem; no running sandbox is required. All paths are relative to the volume root.

<span className="msb-recv">fs.</span><span className="msb-hn">read()</span>

python
async def read(path: str) -> bytes
<Accordion title="Example">
python
handle = await Volume.get("pip-cache")
data = await handle.fs.read("index.json")
</Accordion>

Read the entire contents of a file as raw bytes.

<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 relative to the volume root.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">bytes</span></div> <div className="msb-param-desc">File contents as raw bytes.</div> </div> </div>

<span className="msb-recv">fs.</span><span className="msb-hn">read_text()</span>

python
async def read_text(path: str) -> str

Read the entire contents of a file and decode it as UTF-8.

<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 relative to the volume root.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">str</span></div> <div className="msb-param-desc">File contents as a string.</div> </div> </div>

<span className="msb-recv">fs.</span><span className="msb-hn">write()</span>

python
async def write(path: str, data: bytes) -> None
<Accordion title="Example">
python
handle = await Volume.get("pip-cache")
await handle.fs.write("seed.txt", b"hello")
</Accordion>

Write content to a file, creating it if it doesn't exist and overwriting if it does.

<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 relative to the volume root.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>data</code><span className="msb-type">bytes</span></div> <div className="msb-param-desc">File content.</div> </div> </div>

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

python
async def list(path: str) -> list[FsEntry]

List the entries in a directory.

<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 relative to the volume root.</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="/sdk/python/filesystem#fsentry">list[FsEntry]</a></div> <div className="msb-param-desc">Directory entries.</div> </div> </div>

<span className="msb-recv">fs.</span><span className="msb-hn">mkdir()</span>

python
async def mkdir(path: str) -> None

Create a directory and all parent directories.

<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 relative to the volume root.</div> </div> </div>

<span className="msb-recv">fs.</span><span className="msb-hn">remove_file()</span>

python
async def remove_file(path: str) -> None

Remove a file.

<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 relative to the volume root.</div> </div> </div>

<span className="msb-recv">fs.</span><span className="msb-hn">exists()</span>

python
async def exists(path: str) -> bool

Check whether a path exists within the volume.

<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 relative to the volume root.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">bool</span></div> <div className="msb-param-desc"><code>True</code> if the path exists.</div> </div> </div>

Types

VolumeHandle

<p className="msb-backref">Returned by <a href="#volume-get">Volume.get()</a>, <a href="#volume-list">Volume.list()</a></p>

A lightweight handle to a named volume, with its database metadata and a host-side filesystem accessor.

Property / MethodTypeDescription
namestrVolume name
kindstrVolume kind: dir or disk
quota_mibint | NoneStorage quota in MiB
used_bytesintCurrent disk usage in bytes
capacity_bytesint | NoneDisk capacity in bytes
disk_formatstr | NoneDisk image format
disk_fstypestr | NoneDisk filesystem type
labelsdict[str, str]Metadata labels
created_atfloat | NoneCreation timestamp (ms since epoch)
fsVolumeFsHost-side filesystem handle
remove()(async) NoneDelete this volume

VolumeFs

<p className="msb-backref">Returned by <a href="#volumehandle">VolumeHandle.fs</a></p>

Host-side filesystem operations on a named volume. No running sandbox is required. See the VolumeFs methods above for the full API.

MethodSignatureDescription
readread(path) -> bytesRead a file as raw bytes
read_textread_text(path) -> strRead a file as UTF-8 text
writewrite(path, data) -> NoneWrite bytes to a file
listlist(path) -> list[FsEntry]List a directory
mkdirmkdir(path) -> NoneCreate a directory
remove_fileremove_file(path) -> NoneRemove a file
existsexists(path) -> boolCheck a path exists

MountConfig

<p className="msb-backref">Returned by <a href="#volume-bind">Volume.bind()</a>, <a href="#volume-named">Volume.named()</a>, <a href="#volume-tmpfs">Volume.tmpfs()</a>, <a href="#volume-disk">Volume.disk()</a></p>

Frozen dataclass representing a mount configuration. Build one with a mount factory and pass it as a value in the sandbox volumes dict. stat_virtualization and host_permissions apply only to virtiofs-backed mounts (BIND and NAMED); setting either on a TMPFS or DISK mount raises ValueError.

FieldTypeDefaultDescription
kindMountKind-Type of mount (required)
bindstr | NoneNoneHost path for bind mounts
namedstr | NoneNoneVolume name for named mounts
named_mode"existing" | "create" | "ensure-exists" | NoneNoneNamed-volume creation behavior
named_kind"dir" | "directory" | "disk" | NoneNoneStorage kind for created named volumes
quota_mibint | NoneNoneQuota in MiB for directory named volumes
size_mibint | NoneNoneSize limit for tmpfs, or capacity for disk named volumes
readonlyboolFalseWhether the mount is read-only
noexecboolFalseWhether direct execution from the mount is disabled
nosuidboolFalseWhether setuid/setgid privilege elevation is ignored
nodevboolFalseWhether device files on the mount are ignored
diskstr | NoneNoneHost path to a disk image for disk mounts
formatDiskImageFormat | str | NoneNoneDisk image format for disk mounts
fstypestr | NoneNoneInner filesystem type for disk mounts
stat_virtualizationStatVirtualization | str | NoneNonePer-mount stat-virtualization policy (virtiofs-backed only)
host_permissionsHostPermissions | str | NoneNonePer-mount host-permission policy (virtiofs-backed only)

MountKind

<p className="msb-backref">Used by <a href="#mountconfig">MountConfig</a></p>

String enum (StrEnum) for the type of mount.

ValueDescription
"bind"Host bind mount
"named"Named volume mount
"tmpfs"In-memory filesystem
"disk"Host disk image mount

DiskImageFormat

<p className="msb-backref">Used by <a href="#volume-disk">Volume.disk()</a>, <a href="#mountconfig">MountConfig</a></p>

String enum (StrEnum) for the format of a backing disk image.

ValueDescription
"qcow2"QEMU copy-on-write v2 image
"raw"Raw disk image
"vmdk"VMware disk image