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

# Mounting volumes

> Attach persistent volumes to machines with mount paths, subpaths, and read-only access.

Mount volumes when a machine needs durable shared files. You can declare
volumes at machine create time or attach them to an already running machine
with the SDK or HTTP API. A machine can have up to 8 active volume attachments.

The create-time examples show the Python SDK form first and the equivalent CLI
create command second. Runtime examples use the Python SDK and raw HTTP API.

## Mount A Volume

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  from nullspace import Machine, Volume

  volume = Volume.from_name("team-data", create_if_missing=True)

  with Machine.create(
      template="base",
      volumes=[volume.mount("/workspace/shared")],
  ) as machine:
      machine.files.write("/workspace/shared/hello.txt", "persistent\n")
  ```

  ```bash CLI theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  nullspace machine create \
    --template base \
    --volume ref=team-data,mount=/workspace/shared
  ```
</CodeGroup>

Mount paths must be absolute machine paths outside runtime-managed paths such
as `/workspace/.nullspace`.

## Mount A Subpath

Expose only one directory from the volume:

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  mount = volume.mount("/workspace/datasets", subpath="/datasets")
  machine = Machine.create(template="base", volumes=[mount])
  ```

  ```bash CLI theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  nullspace machine create \
    --template base \
    --volume ref=team-data,mount=/workspace/datasets,subpath=/datasets
  ```
</CodeGroup>

`subpath` is resolved inside the volume and exposes only that subtree at the
machine mount path.

## Read-Only Mounts

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  mount = volume.mount("/workspace/models", subpath="/published-models", read_only=True)
  machine = Machine.create(template="base", volumes=[mount])
  ```

  ```bash CLI theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  nullspace machine create \
    --template base \
    --volume ref=team-data,mount=/workspace/models,subpath=/published-models,ro=true
  ```
</CodeGroup>

`read_only=True` prevents writes through the machine mount. Callers with API
access can still modify the volume through direct `volume.files` or
`nullspace volume ...` operations.

## Dictionary Mounts

SDK calls also accept dictionaries, which is useful when mount configuration is
loaded from JSON:

```python theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
machine = Machine.create(
    template="base",
    volumes=[
        {
            "ref": "team-data",
            "mount_path": "/workspace/shared",
            "subpath": "/datasets",
            "read_only": True,
        },
    ],
)
```

`ref` can be a volume name or volume ID.

## Runtime Attach And Detach

Attach a volume to a running machine with the same mount descriptor shape used
at create time:

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  from nullspace import Machine, Volume

  machine = Machine.connect("mch_...")
  volume = Volume.from_name("team-data")

  attachment = machine.attach_volume(volume.id, "/workspace/shared")
  print(attachment.id, attachment.state, attachment.mount_path)
  ```

  ```bash HTTP API theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  curl -fsS -X POST "${NULLSPACE_API_URL}/v1/machines/${MACHINE_ID}/volumes" \
    -H "Authorization: Bearer ${NULLSPACE_API_KEY}" \
    -H "Content-Type: application/json" \
    --data '{"ref":"team-data","mount_path":"/workspace/shared"}'
  ```
</CodeGroup>

List the current attachments for a machine or a volume:

```python theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
for attachment in machine.list_volumes():
    print(attachment.id, attachment.volume_id, attachment.state)

for attachment in volume.attachments():
    print(attachment.machine_id, attachment.mount_path, attachment.state)
```

The equivalent HTTP list routes are `GET /v1/machines/{id}/volumes` and
`GET /v1/volumes/{id}/attachments`.

Detach one runtime attachment by ID:

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  released = machine.detach_volume(attachment.id)
  print(released.state)
  ```

  ```bash HTTP API theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  curl -fsS -X DELETE \
    "${NULLSPACE_API_URL}/v1/machines/${MACHINE_ID}/volumes/${ATTACHMENT_ID}" \
    -H "Authorization: Bearer ${NULLSPACE_API_KEY}"
  ```
</CodeGroup>

The Python SDK accepts a volume ID, volume name, `VolumeMount`, or dictionary
for `machine.attach_volume(...)`. If `read_only` is omitted, the runtime API
defaults it to `false`.

## Health And Remount

Use health and remount operations when a mounted volume is degraded or after an
operator asks you to refresh the live mount:

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  health = machine.get_volume_health(attachment.id)
  if health.state == "failed":
      print(health.last_error_code, health.last_error_message)

  remounted = machine.remount_volume(attachment.id)
  print(remounted.state)
  ```

  ```bash HTTP API theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  curl -fsS \
    "${NULLSPACE_API_URL}/v1/machines/${MACHINE_ID}/volumes/${ATTACHMENT_ID}/health" \
    -H "Authorization: Bearer ${NULLSPACE_API_KEY}"

  curl -fsS -X POST \
    "${NULLSPACE_API_URL}/v1/machines/${MACHINE_ID}/volumes/${ATTACHMENT_ID}/remount" \
    -H "Authorization: Bearer ${NULLSPACE_API_KEY}"
  ```
</CodeGroup>

Attachment states include `requested`, `mounting`, `mounted`, `paused`,
`remounting`, `unmounted`, `releasing`, `released`, and `failed`.

Destroy the machine to release all remaining volume attachments. Hibernating or
pausing a machine releases the live mount lease while the VM is stopped, but it
preserves the attachment intent; resume remounts the same volumes. Fork and
snapshot restore also remount the stored attachments in the new machine.

The CLI currently supports create-time `--volume` entries. Use the SDK or HTTP
API for runtime attach, detach, health, and remount operations.

## Snapshot Behavior

Shared volume data is durable storage external to VM memory and mutable rootfs
snapshots. Hibernate, resume, and fork remount volume attachments; they do not
copy volume data into the VM snapshot.

## Related

* [Managing volumes](./managing-volumes)
* [Read & write](./read-write)
* [Create machine with CLI](../cli/create-machine)
