Volumes
Persistent file storage mounted into functions and apps with rb.Volume.
Most data belongs in a Bucket, not a Volume.
A volume is a gcsfuse mount over object storage, and the mount makes expensive operations look cheap. Editing one byte of a 1 GB file downloads and re-uploads the whole gigabyte. Rename is copy-then-delete, not atomic. There is no file locking, and concurrent writers silently overwrite each other.
Reach for a Volume when a library you do not control insists on a real filesystem path,
and the data is read-mostly — model weights, checkpoints written once per epoch. For
everything else use rb.Bucket, which is honest about being an object store.
Volumes give deployed code a persistent, shared filesystem for the cases that genuinely need one: read-mostly data that a library insists on opening from a real path — model weights, checkpoints, a cache directory. They mirror Modal's volumes: create one lazily, mount it at a path, read and write ordinary files.
import rebase as rb
vol = rb.Volume.from_name("model-cache", create_if_missing=True)
@rb.function(project="energy", mode="job", volumes={"/models": vol})
def train(site_id: str = "site-001") -> dict:
model = fit_model(site_id)
with open("/models/site-001.pkl", "wb") as f:
pickle.dump(model, f)
return {"stored": "/models/site-001.pkl"}
@rb.function(
project="energy",
isolation="dedicated",
volumes={"/models": rb.Volume.from_name("model-cache", read_only=True)},
)
def predict(site_id: str = "site-001") -> dict:
with open("/models/site-001.pkl", "rb") as f:
model = pickle.load(f)
return {"forecast": model.predict()}Volumes mount for functions with isolation="dedicated" or mode="job", and for ASGI apps.
The shared interactive runner (the default isolation="shared") receives no volume mounts, so a
function running there does not see the path.
Workflow steps execute inside the Prefect worker in v1 and cannot mount volumes yet.
Working with Files from Your Machine
The SDK moves bytes over presigned URLs — directly between you and object storage, never through the platform API:
vol = rb.Volume.from_name("model-cache")
vol.put_file("local/model.pkl", "site-001.pkl")
vol.put_directory("training-runs/", "runs")
vol.listdir() # [{"path": "site-001.pkl", "size": ..., "updated": ...}]
data = vol.read_file("site-001.pkl")
vol.get_file("site-001.pkl", "downloaded.pkl")
vol.remove_file("site-001.pkl")The same operations are available from the CLI:
rebase volume create model-cache
rebase volume list
rebase volume ls model-cache
rebase volume put model-cache local/model.pkl site-001.pkl
rebase volume download model-cache site-001.pkl
rebase volume rm model-cache site-001.pkl
rebase volume delete model-cache # removes the volume AND all filesConsistency Model
Volumes are backed by object storage (GCS in v1) mounted with gcsfuse. Writes are
visible to other readers when the file is closed — there is no commit() step.
Volume.commit() and Volume.reload() exist as no-ops for Modal compatibility.
Guidelines:
- Treat files as whole objects: write-then-close, read-after-open. Appending and in-place mutation of large files perform poorly on FUSE-mounted object storage.
- Concurrent writes to the same file are last-write-wins. Give concurrent writers distinct paths (e.g. one file per site or per run).
- Mount
read_only=Truewherever code only reads — it is both safer and faster.
Storage Layout
Each workspace gets one storage bucket; each volume is a prefix inside it, and
containers see only their own volume via scoped mounts. Volume records carry a
provider field (gcs today) so alternative backends can be added without changing
the SDK surface.
Deleting a volume deletes every file in it. There is no undelete.
Permissions
Volumes use volumes:read and volumes:write. Both are granted to the Developer
role, so the people deploying code that mounts a volume can also create and fill it —
Volume.from_name(..., create_if_missing=True) creates the volume at deploy time, so
without volumes:write the deploy itself fails. Viewers get volumes:read.
API keys minted before these permissions existed carry workspace:read and
workspace:update instead; both are still accepted, so those keys keep working
without a reissue.

