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

# Mount Filesystems

> Mount durable, shareable Tensorlake filesystems into sandboxes at boot, on warm-pool claims, or on a running sandbox.

A sandbox can mount [Tensorlake filesystems](/filesystems/introduction) as ordinary directories. The sandbox's root disk is ephemeral; a mounted filesystem is durable, survives the sandbox, and can be shared by many sandboxes at once. Reads stream in lazily, so mounting is fast regardless of how much the filesystem holds, and writes replicate to durable storage automatically through [autosave](/filesystems/concurrent-writes).

You mount a filesystem by the name you created it with:

```bash theme={null}
tl fs create data
```

That name (`data`) is the `file_system_id` everywhere in the sandbox API.

## Mount at Creation

Pass one or more mounts when creating the sandbox. The mounts are ready before the sandbox is reported as running.

<Tabs>
  <Tab title="CLI">
    ```bash theme={null}
    tl sbx create -f data:/mnt/data
    ```

    `-f`/`--filesystem` takes `<name>:<mount-path>[:<opts>]` and can be repeated. `<opts>` is a comma-separated list of `ro` (read-only) and/or `prefetch`:

    ```bash theme={null}
    tl sbx create -f data:/mnt/data:ro,prefetch -f scratch:/work
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from tensorlake.sandbox import FileSystemMount, Sandbox

    with Sandbox.create(
        image="tensorlake/ubuntu-minimal",
        file_systems=[
            FileSystemMount(file_system_id="data", mount_path="/mnt/data"),
        ],
    ) as sandbox:
        result = sandbox.run("ls", ["/mnt/data"])
        print(result.stdout)
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    import { Sandbox } from "tensorlake";

    const sandbox = await Sandbox.create({
      image: "tensorlake/ubuntu-minimal",
      fileSystems: [{ fileSystemId: "data", mountPath: "/mnt/data" }],
    });

    const result = await sandbox.run("ls", { args: ["/mnt/data"] });
    console.log(result.stdout);

    await sandbox.terminate();
    ```
  </Tab>

  <Tab title="HTTP">
    ```bash theme={null}
    curl -X POST https://api.tensorlake.ai/sandboxes \
      -H "Authorization: Bearer $TL_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "file_systems": [
          {"file_system_id": "data", "mount_path": "/mnt/data"}
        ]
      }'
    ```

    Each mount accepts optional `read_only` and `prefetch` booleans; omitting them means `false`.
  </Tab>
</Tabs>

<Note>
  Create the filesystem first (`tl fs create <name>`). Mounting a name that does not exist fails the sandbox — see [Errors](#errors).
</Note>

### Mount rules

| Rule       | Detail                                                                                                                           |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Name       | `file_system_id` is the filesystem's name (ASCII letters, digits, `_`, `-`)                                                      |
| Path       | `mount_path` must be absolute and not `/`; paths are normalized (`//`, `.`, and trailing slashes collapse), and `..` is rejected |
| Uniqueness | No two mounts may share a path or nest under one another (`/mnt` and `/mnt/data` conflict)                                       |
| Count      | At most 8 filesystem mounts per sandbox                                                                                          |

## Mount Options

Both options are per mount and default to off.

### Read-only

`read_only` mounts the filesystem read-only. Writes inside the guest fail with `EROFS`. Enforcement is defense-in-depth: the storage credential minted for the mount carries no write scope, the host proxy filters writes, and the guest mount itself is read-only.

Read-only is fail-closed: the sandbox is only placed on executor fleets that can enforce it, so a `read_only` mount can never silently degrade to read-write. On fleets that have not yet been updated, a sandbox requesting a read-only mount will not be placed.

### Prefetch

`prefetch` downloads the filesystem's full tree in the background after the mount is ready. The mount is usable immediately — reads stream in lazily in the meantime — and once the prefetch completes, reads no longer touch the network. Prefetch is best-effort: it never blocks mount readiness and never fails the sandbox. On older fleets it is skipped silently.

<Tabs>
  <Tab title="CLI">
    ```bash theme={null}
    tl sbx create -f models:/mnt/models:ro,prefetch
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from tensorlake.sandbox import FileSystemMount, Sandbox

    sandbox = Sandbox.create(
        file_systems=[
            FileSystemMount(
                file_system_id="models",
                mount_path="/mnt/models",
                read_only=True,
                prefetch=True,
            ),
        ],
    )
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const sandbox = await Sandbox.create({
      fileSystems: [
        {
          fileSystemId: "models",
          mountPath: "/mnt/models",
          readOnly: true,
          prefetch: true,
        },
      ],
    });
    ```
  </Tab>

  <Tab title="HTTP">
    ```bash theme={null}
    curl -X POST https://api.tensorlake.ai/sandboxes \
      -H "Authorization: Bearer $TL_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "file_systems": [
          {
            "file_system_id": "models",
            "mount_path": "/mnt/models",
            "read_only": true,
            "prefetch": true
          }
        ]
      }'
    ```
  </Tab>
</Tabs>

Both options require the latest SDK/CLI and up-to-date executor fleets.

## Mount on a Warm-Pool Claim

[Pools](/sandboxes/pools) keep containers pre-booted without filesystems; mounts belong to the claim, not the pool. Pass the same `file_systems` when claiming, and the mounts are ready before the claimed sandbox is reported as running.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from tensorlake.sandbox import FileSystemMount, Sandbox

    sandbox = Sandbox.create(
        pool_id=pool.pool_id,
        file_systems=[
            FileSystemMount(file_system_id="data", mount_path="/mnt/data"),
        ],
    )
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const sandbox = await Sandbox.create({
      poolId: pool.poolId,
      fileSystems: [{ fileSystemId: "data", mountPath: "/mnt/data" }],
    });
    ```
  </Tab>

  <Tab title="HTTP">
    ```bash theme={null}
    curl -X POST https://api.tensorlake.ai/sandbox-pools/<pool-id>/sandboxes \
      -H "Authorization: Bearer $TL_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "file_systems": [
          {"file_system_id": "data", "mount_path": "/mnt/data"}
        ]
      }'
    ```

    The response includes `claim_configuration_applied: true`, which confirms the server decoded and persisted the claim's mounts. SDKs check this to fail closed against older servers that would accept but ignore the body.
  </Tab>
</Tabs>

## Attach and Detach at Runtime

A running sandbox can attach and detach filesystems without restarting.

<Tabs>
  <Tab title="CLI">
    ```bash theme={null}
    # Attach (optionally --read-only and/or --prefetch)
    tl sbx fs attach <sandbox-id> --id data --path /mnt/data

    # List current mounts
    tl sbx fs ls <sandbox-id>

    # Detach by mount path
    tl sbx fs detach <sandbox-id> --path /mnt/data
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    sandbox.attach_file_system("data", "/mnt/data", read_only=False, prefetch=False)

    for mount in sandbox.list_file_systems():
        print(mount.file_system_id, mount.mount_path)

    sandbox.detach_file_system("/mnt/data")
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    await sandbox.attachFileSystem("data", "/mnt/data", {
      readOnly: false,
      prefetch: false,
    });

    for (const mount of await sandbox.listFileSystems()) {
      console.log(mount.fileSystemId, mount.mountPath);
    }

    await sandbox.detachFileSystem("/mnt/data");
    ```
  </Tab>

  <Tab title="HTTP">
    ```bash theme={null}
    # Attach
    curl -X POST https://api.tensorlake.ai/sandboxes/<sandbox-id>/file_systems \
      -H "Authorization: Bearer $TL_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"file_system_id": "data", "mount_path": "/mnt/data"}'

    # Detach
    curl -X DELETE https://api.tensorlake.ai/sandboxes/<sandbox-id>/file_systems \
      -H "Authorization: Bearer $TL_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"mount_path": "/mnt/data"}'
    ```

    A `200` means the change is persisted; the mount or unmount applies asynchronously on the live sandbox moments later.
  </Tab>
</Tabs>

<Warning>
  Runtime attach is fail-closed: if an attached mount later cannot converge — most commonly because the filesystem does not exist — the whole sandbox is terminated, with the reason and an actionable `error_details` message on the sandbox object. Verify the filesystem exists (`tl fs ls`) before attaching to a sandbox whose state you care about, or mount at creation time instead, where the same mistake fails only the create.
</Warning>

Two other runtime-attach responses to know about:

* **`400`** — the sandbox runs on an executor fleet without filesystem support. Recreate the sandbox to mount filesystems.
* **`409`** — the mount path is already in use, the sandbox is not running, or the sandbox's executor is momentarily unresolvable (for example during a reconnect window). The last case is transient: retry shortly.

## Errors

Mounting a filesystem that does not exist fails sandbox creation with HTTP `422`:

```json theme={null}
{
  "sandbox_id": "sbx-...",
  "status": "failed",
  "reason": "FileSystemNotFound"
}
```

The sandbox object records the same reason with a message telling you exactly what to do:

```json theme={null}
{
  "status": "terminated",
  "termination_reason": "FileSystemNotFound",
  "error_details": "File system 'data' was not found in this project. Create it with 'tl fs create data' before mounting it at /mnt/data."
}
```

Create the filesystem first, then create the sandbox:

```bash theme={null}
tl fs create data
tl sbx create -f data:/mnt/data
```

## Sharing Across Sandboxes

The same filesystem can be mounted by multiple sandboxes in one project concurrently. Writes from one sandbox become visible to the others as autosave checkpoints replicate — see [Concurrent Writes](/filesystems/concurrent-writes) for the merge semantics and [Distribute Files](/filesystems/distribute-files) for rolling out shared assets to a fleet of sandboxes with read-only mounts.

## Related Guides

<CardGroup cols={2}>
  <Card title="Filesystems" icon="hard-drive" href="/filesystems/introduction">
    Durable, versioned filesystems: create, push, snapshot, and time-travel.
  </Card>

  <Card title="Read-only Mounts" icon="lock" href="/filesystems/read-only-mounts">
    Pinned and following mounts for fixed inputs and shared assets.
  </Card>

  <Card title="Sandbox Pools" icon="layer-group" href="/sandboxes/pools">
    Pre-warm sandboxes and mount filesystems at claim time.
  </Card>

  <Card title="File Operations" icon="folder-open" href="/sandboxes/file-operations">
    Copy, read, and write files on the sandbox's ephemeral root disk.
  </Card>
</CardGroup>
