A durable, versioned file system that tasks mount and read and write like a local directory, with cheap copy-on-write forks.
Volumes
A Volume is a durable file system that your task mounts and uses like an ordinary local directory, backed by object storage, but with real file-system semantics: open, read, write, list, and seek over many files in place.
Unlike flyte.io.File and flyte.io.Dir, which pass
a snapshot of data as a value between tasks, a Volume is long-lived and
versioned. You write to it during one run and commit it as an immutable
version; any later task or run can mount that version and pick up exactly where
you left off. Each commit is a new immutable version, and versions share
unchanged data, so keeping history is cheap.
Volumes vs. files and directories
File/Dir and Volumes solve different problems. Use the one that matches how
your data is shaped and used.
flyte.io.File / flyte.io.Dir |
Volume | |
|---|---|---|
| What it is | A single file or folder passed as a value | A whole file system you mount |
| Access | Uploaded/downloaded as a unit | Mounted; read and written in place, like a local disk |
| Lifetime | Tied to the run that produced it | Long-lived; remount across tasks and runs |
| Changing it | Produce a new File/Dir |
Fork, write, and commit a new version (copy-on-write) |
| Best for | Handing a finished artifact to the next task | Evolving, file-system-heavy state |
If you just need to hand a finished file or folder from one task to the next,
reach for flyte.io.File or flyte.io.Dir. They’re
simpler and need no setup. Choose a Volume when you need a mountable, durable
file system that evolves over time.
A Volume is a durable, network-backed file system, not an in-memory cache. Use it when you want file-system semantics over durable, shared data (mounting, partial and random reads, tools that expect files on disk). It is not a way to speed up model loading: pulling weights into memory through a Volume is slower than streaming them directly from object storage with a purpose-built loader.
When to use a Volume
Volumes fit AI and agentic workloads, where work is long-running, stateful, and file-heavy:
- Agent memory and state. Give an agent a durable workspace it builds up across turns, tasks, and sessions (notes, intermediate artifacts, a growing working set of files) and resume exactly where it left off, instead of starting cold each run.
- Sandboxes and code execution. Back a sandbox or code-execution environment with a Volume so agent- or model-generated code has a real, writable file system to work in. Fork a clean base per session so concurrent runs stay isolated from each other.
- Shared, durable datasets. Keep a dataset, index, or other large working set on a Volume and mount it from many tasks to read (or fork and update) it as files, without re-fetching or re-uploading the whole thing each run.
- Branching experiments. Fork a base Volume per experiment or per run; copy-on-write makes each branch independent and cheap, with version history to compare against or roll back to.
More broadly, reach for a Volume whenever you need long-lived, versioned state that carries forward across tasks or runs: anything you’d otherwise rebuild from scratch every time.
Build caches, package caches, virtualenvs and source trees spend most of their time on per-file metadata. A block volume stores the volume as one ext4 image the kernel mounts directly, which brings these workloads close to local-disk speed.
Read-write and read-only volumes
A Volume is always one of two types, and the type tells you what you can do with it:
RWVolume: a writable handle.Volume.new()returns one. Mount it, write to it, andcommit()to record an immutable version. While it is mounted it is the single writer.ROVolume: an immutable, committed version. Mount it read-only to read its contents. To change it,fork()it into a newRWVolume.
Because the type is part of a task’s signature, the read/write contract is
enforced at the task boundary: a task that declares vol: ROVolume can read
shared data but cannot mutate it. Returning a writable RWVolume from a task
commits it and hands the next task an ROVolume.
Setup
Volumes are mounted inside the task pod, so the task environment needs two
things: an image with the volume client (flyteplugins-union), and a pod
template that lets the pod reach the mount.
import flyte
from flyteplugins.union.io import Volume, ROVolume, allow_volumes
image = (
flyte.Image.from_debian_base()
.with_pip_packages("flyteplugins-union") # volume client (bundles the mount binary)
)
env = flyte.TaskEnvironment(
name="volumes-demo",
image=image,
# let the pod mount Volumes (no privileges required)
pod_template=allow_volumes(),
resources=flyte.Resources(cpu="1", memory="2Gi"),
)allow_volumes() (from flyteplugins.union.io) is the only pod-level
setup a Volume needs, and the mount runs fully unprivileged: no
CAP_SYS_ADMIN, no /dev/fuse, no fuse3 package.
It relies on a mount broker running on the cluster. The Union data plane ships one; on a self-managed cluster an administrator enables it.
Where to go next
The rest of this topic is split by what you are trying to do. The quickest way to get your hands on a volume is the first one: it needs no cluster.
Reference
- API:
Volume,RWVolume,ROVolume, andflyteplugins.union.io.allow_volumes. - Artifact publication:
Volume.new(artifact=...),fork(artifact=...), andpublish_artifact=/artifact_version=oncommit()andfinalize()- see Tracking versions as artifacts. - Reporting:
vol.report = Trueor$UNION_VOLUME_REPORT- see Debugging a mount. - Caching:
allow_volumes(cache_size=...)andmount(cache_dir=..., cache_size_mb=...)- see The chunk cache;allow_volumes(shared_node_cache=True)andmount(shared_node_cache=...)- see Shared node cache. - Client memory:
$UNION_VOLUME_CLIENT_MEMORY_LIMIT. See Tuning the mount. - CLI:
flyte create volume,flyte mount volume,flyte warmup volume,flyte status volume,flyte unmount volume,flyte forget volume- see Work with a volume on your machine;flyte explore volume- see Inspecting a volume. - Related: Block volumes for small-file, single-writer workloads; Files and directories for passing snapshot data between tasks.