Volumes¶
A Volume maps object storage or provider-native storage to a directory on each compute node. The compute and the stored data have independent lifecycles.
Volume¶
Construct volumes with bucket=, never with a display name:
import skyward as sky
data = sky.Volume(
bucket="my-datasets",
mount="/data",
prefix="imagenet/train/",
read_only=True,
)
checkpoints = sky.Volume(
bucket="my-experiments",
mount="/checkpoints",
read_only=False,
)
Fields:
bucket: bucket name, or a provider-native volume id/name where supported;mount: absolute path on every node;prefix: optional subdirectory within the bucket;read_only: defaults toTrue;storage: optional explicitsky.Storageendpoint and credentials.
The constructor rejects relative paths and protected system paths such as /, /root, /tmp, and /opt.
Attach volumes to a compute¶
Pass volumes to Compute:
import skyward as sky
@sky.function
def train(data_dir: str, checkpoint_dir: str) -> None:
dataset = load(data_dir)
model = fit(dataset)
save(model, f"{checkpoint_dir}/model.pt")
with sky.Compute(
provider=sky.AWS(),
accelerator=sky.accelerators.A100(),
nodes=2,
volumes=[
sky.Volume(bucket="my-datasets", mount="/data"),
sky.Volume(bucket="my-experiments", mount="/checkpoints", read_only=False),
],
) as compute:
train("/data", "/checkpoints") >> compute
The function sees ordinary filesystem paths. The provider adapter decides how the mount is prepared before the worker starts.
If storage is omitted, the daemon asks the compute's provider to resolve the storage endpoint. Use storage= when the bucket belongs to a different storage account or endpoint.
S3-compatible storage¶
Storage is a context manager for local CRUD operations and can also be passed to a Volume:
import skyward as sky
r2 = sky.storage.R2(
account_id="account-id",
access_key="access-key",
secret_key="secret-key",
)
with sky.Compute(
provider=sky.AWS(),
volumes=[sky.Volume(bucket="training-data", mount="/data", storage=r2)],
) as compute:
...
Available presets and their signatures are:
sky.storage.S3(
region="us-east-1",
access_key=None,
secret_key=None,
)
sky.storage.GCS(access_key="...", secret_key="...")
sky.storage.R2(account_id="...", access_key="...", secret_key="...")
sky.storage.Wasabi(region="...", access_key="...", secret_key="...")
sky.storage.Backblaze(region="...", key_id="...", app_key="...")
sky.storage.Hyperstack(
access_key="...",
secret_key="...",
endpoint="https://ca1.obj.nexgencloud.io",
)
Or construct an endpoint directly:
storage = sky.Storage(
endpoint="https://s3.example.com",
access_key="...",
secret_key="...",
path_style=False,
)
Open a Storage before using its local CRUD methods:
with storage:
storage.upload("my-bucket", "./data.csv", key="data.csv")
storage.download("my-bucket", "data.csv", "./copy.csv")
keys = storage.ls("my-bucket", prefix="runs/")
exists = storage.exists("my-bucket", "data.csv")
storage.rm("my-bucket", "old/data.csv")
upload, download, ls, exists, and rm are synchronous methods on the context-managed object.
Credentials may be strings, synchronous callables, or asynchronous callables.
Provider strategies¶
These providers have volume mount adapters:
| Provider | Current strategy |
|---|---|
| AWS | S3-compatible mount using the machine identity |
| GCP | S3-compatible mount with a per-compute HMAC key |
| Hyperstack | S3-compatible mount with a per-compute object-storage key |
| RunPod | One provider network-volume attachment, projected through prefix |
Providers without a mount adapter report a capability mismatch when a compute requests volumes. The provider adapter owns the bootstrap details; the public API remains Volume(bucket=..., mount=...).
RunPod accepts one network volume per pod. Multiple Volume objects can share that bucket and use different prefixes:
with sky.Compute(
provider=sky.RunPod(),
volumes=[
sky.Volume(bucket="checkpoints", mount="/train", prefix="train"),
sky.Volume(bucket="checkpoints", mount="/eval", prefix="eval"),
],
) as compute:
...
Shared buckets¶
Multiple volumes can reference the same bucket. The bootstrap deduplicates the underlying FUSE mount and creates one link per mount/prefix pair. If any volume for that bucket is writable, the shared mount is writable.
volumes = [
sky.Volume(bucket="datasets", mount="/train", prefix="train"),
sky.Volume(bucket="datasets", mount="/validation", prefix="validation"),
]
There is no volume configuration file. Declare volumes in Compute.
Next steps¶
- S3 volumes — a walkthrough with runnable code
- Providers — which providers mount buckets and which attach volumes
- CLI — inspecting a compute's mounts from the terminal