# Volumes

> Keep datasets, model weights and working files in Volumes, upload and download them, import from Hugging Face, git, URLs or S3, and mount them in Jobs and Sandboxes.

Source: https://nodus-platform-site.pages.dev/docs/guides/volumes/
Build revision: 211ad9f836655b1c3a2668c4693e442471f28614

A Volume is named storage that outlives the work that uses it. Every change is saved as a numbered revision, so you can see what changed, mount an earlier revision read-only, and never lose files to a stopped machine. You pay for the bytes stored, after your org’s included 10 GB.

## Create a Volume and upload files

volume.yaml

```yaml
apiVersion: nodus.dev/v1
kind: Volume
metadata:
  name: ex-volumes-put-get
spec:
  accessMode: ReadWriteOnce
  size: 1Gi
  source: {upload: {}}
```

Terminal window

```console
nodus apply -f volume.yaml                        # or: nodus create volume data --size 100Gi
nodus volume put ex-volumes-put-get ./data /data  # uploads ./data and prints the new revision
nodus volume ls ex-volumes-put-get /data
nodus volume get ex-volumes-put-get /data/hello.txt ./out/
nodus volume put data ./archive.tar.gz /raw --extract
nodus volume rm data /raw/old.csv
nodus volume clear data                           # an empty revision; the Volume and its history stay
```

Uploads only send what changed: a second `put` of the same files moves almost nothing. An upload that started from an older revision than the latest is refused with `409 Conflict`, so two people uploading at once never overwrite each other silently; run it again on top of the new revision.

`put` adds to what is there: a directory’s contents merge into the remote path, and a file lands at the remote path, or inside it when the remote path is a directory or ends in `/`. `rm` and `clear` also commit new revisions, so earlier revisions keep the removed files. `get` and `ls` read the latest revision, or the one `--revision <n>` names; `get … -` prints a file to standard output, and existing local files are kept unless `--force`. `--extract` expands a `.zip`, `.tar`, `.tar.gz`, `.tgz` or `.tar.zst` archive. It refuses an archive whose paths or links lead outside the remote path, or that has more than 10,000 members.

## Mount it in a Job

job.yaml

```yaml
apiVersion: nodus.dev/v1
kind: Job
metadata:
  name: ex-volumes-put-get
spec:
  image: nodus/python:3.12
  volumes:
    - {volume: ex-volumes-put-get, mountPath: /mnt/vol, readOnly: true}
  command: [cat, /mnt/vol/data/hello.txt]
```

|`accessMode`|Who can write|How changes are saved|
|-|-|-|
|`ReadWriteOnce` (default)|One attempt at a time|On exit and every `commitInterval` (default 5 minutes)|
|`ReadOnlyMany` (default for imports)|Nobody; any number of readers|Revisions come from imports and uploads|
|`ReadWriteMany`|Every worker|Each worker’s changes are published when it commits or exits; the last writer of a path wins|

A `ReadWriteOnce` Volume is held by one attempt at a time. Starting a second writer, or deleting the Volume while it is held, fails with `409 VolumeBusy`, which names the holder; mount it with `readOnly: true` to read alongside. Functions and Agents with more than one worker need `ReadOnlyMany` or `ReadWriteMany`.

Mount an earlier revision with `revision: <n>` and `readOnly: true`. `nodus get volume data` shows the latest `REVISION`, `USED` and the current `HOLDER`; `GET …/volumes/{name}/revisions` lists the kept revisions (`revisionHistoryLimit`, default 10).

## Import from elsewhere

Set exactly one `source`; the import runs once on Nodus, billed as CPU time, and produces revision 1:

```yaml
spec:
  accessMode: ReadOnlyMany
  size: 200Gi
  maxCostUSD: "2.00"
  source: {huggingface: {repo: meta-llama/Llama-3.1-8B, revision: 0e9e39f249a16976918f6564b8830bc894c89659, secret: hf-token}}
```

|Source|Fields|
|-|-|
|`huggingface`|`repo`, `revision` (a commit or tag), `files` globs, `secret` holding `HF_TOKEN`|
|`git`|`repo`, `ref`, `lfs`|
|`url`|an `https://` `url`, `sha256`, `extract: Auto` unpacks `.zip`, `.tar`, `.tar.gz`, `.tgz` and `.tar.zst`|
|`s3`|`uri` and an `S3` [Connection](https://nodus-platform-site.pages.dev/docs/guides/connections/)|

The Volume is `Pending` while it imports and `Ready` after. A failed import shows `Failed` with reason `ImportFailed` and a message; fix the source or add credits, then run `nodus request reimport volume/<name>`.

## In Python

```python
vol = nodus.Volume.from_name("data", create_if_missing=True)
vol.put_file("./local.csv", "/train/local.csv")
print(vol.listdir("/train"))
```
