DOCS

Volumes

Mount object storage (S3, R2, GCS, Azure Blob, S3-compatible) into sandboxes. Define a volume once, attach it to many sandboxes.

A volume is a reusable, named definition of an object-storage location plus its credentials — much like a template. You create it once, then attach it to as many sandboxes as you like, each at its own mount path. Credentials are encrypted at rest and never returned by the API.

Create a reusable volume

import qbox

vol = qbox.Volumes.create(qbox.S3Volume(
    name="data",                 # lowercase, used in env names: QBOX_VOLUME_data_*
    bucket="my-bucket",
    region="us-east-1",
    access_key_id="AKIA...",
    secret_access_key="...",
))

Typed builders exist for every provider: S3Volume, R2Volume, S3CompatibleVolume, GCSVolume, AzureBlobVolume. Each takes the fields its provider needs (bucket, account id, service-account JSON, etc.).

Attach to sandboxes

# at spawn
sb = qbox.Sandbox.create("python-3.12", volumes=[qbox.Mount("data", "/mnt/data")])

# or on a running sandbox
sb.volumes.attach("data", "/mnt/data")
other.volumes.attach("data", "/data", read_only=True)   # same volume, different sandbox + path

sb.volumes.list()              # attachments + their mount state
sb.volumes.remount("data")     # retry after fixing credentials
sb.volumes.detach("data")

A volume attaches to a given sandbox once. To mount the same bucket at two paths in one sandbox, create two volumes. Across different sandboxes you can mount the same volume at whatever paths you like.

What attaching does

For each attached volume, qbox derives a set of environment variables and makes them available in the sandbox (source="volume" in envs.list()):

QBOX_VOLUME_data_TYPE=s3
QBOX_VOLUME_data_BUCKET=my-bucket
QBOX_VOLUME_data_REGION=us-east-1
QBOX_VOLUME_data_ACCESS_KEY=AKIA...
QBOX_VOLUME_data_SECRET_KEY=...
QBOX_VOLUME_data_MOUNT_PATH=/mnt/data
QBOX_VOLUME_data_READ_ONLY=false

Single-volume convenience: if a sandbox has exactly one S3-flavored volume (s3/r2/s3_compatible) and you haven’t set AWS_ACCESS_KEY_ID yourself, qbox also fills AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_REGION (and AWS_ENDPOINT_URL for R2/S3-compatible) so boto3/aws-cli/rclone just work.

Auto-mount vs. manual

Whether the volume is actually mounted as a filesystem depends on the template’s base image:

ImageBehaviourAttachment state
ships the qbox volumes service (mount-s3, gcsfuse, blobfuse2 + /etc/qbox/services/volumes.json)qbox auto-mounts at the mount pathmounted (or failed with a reason)
no volumes serviceqbox sets the envs + creates the mount point, but doesn’t mountmanual

In the manual case nothing is mounted for you — use the QBOX_VOLUME_* envs to mount it yourself (or boto3/rclone straight against the bucket). The qbox base images (qbox-code-interpreter, …) ship the volumes service; a plain FROM debian:bookworm template does not.

Mounts are unmounted cleanly when the sandbox terminates, flushing pending writes back to the bucket.

CLI

qbox volumes create data --type s3 \
  -c bucket=my-bucket -c region=us-east-1 -c access_key_id=AKIA... -c secret_access_key=...
qbox volumes list
qbox volumes delete data --force          # --force if attached anywhere

qbox sandboxes volumes attach sb_123 data --mount-path /mnt/data
qbox sandboxes volumes list sb_123
qbox sandboxes volumes detach sb_123 data

Providers & config fields

TypeRequired config
s3bucket, region, access_key_id, secret_access_key (optional endpoint, prefix, session_token)
r2bucket, account_id, access_key_id, secret_access_key (endpoint derived from account_id)
s3_compatiblebucket, endpoint (no path), access_key_id, secret_access_key (optional region, force_path_style)
gcsbucket, service_account_key_json
azure_blobaccount, container, account_key

S3-compatible endpoints work with MinIO, DigitalOcean Spaces, Backblaze B2, Linode, Hetzner, etc. — set endpoint to the service’s S3 URL.

HTTP & scopes

Reusable volumes live at /v1/volumes (GET/POST, GET/PATCH/DELETE /{id}); attachments at /v1/sandboxes/{id}/volumes. Reading metadata needs sandboxes:read; creating/attaching needs the sandboxes:volumes scope. The API never returns secret credentials — only a non-secret configSummary (bucket, region, endpoint). To rotate credentials, PATCH the volume with a full new config; every sandbox it’s attached to is re-pushed.