Storage Backends

Backend objects are content-addressed by SHA-256 - the storage engine doesn't care where the bytes physically live, only that the backend can put, get, delete, and exists by hash. Switch backends by changing one line in anvil.toml.

Available backends

BackendUseStatus
localFiles under data/storage/{hash[0:2]}/{hash[2:4]}/{hash}Production
s3AWS S3 (or any S3-compatible: R2, MinIO, Wasabi, B2)Stub - Phase 25.4 deferred
gcsGoogle Cloud StorageStub
azureAzure Blob StorageStub

Local backend

[file_storage]
backend = "local"
file_storage_local_root = "./data/storage"
staging_dir = "./data/staging"        # temp uploads go here first

Files are content-addressed: identical bytes are stored once and shared via the in-memory refcounter. The backend never deletes a file while its refcount > 0.

S3-compatible (when enabled)

[file_storage]
backend = "s3"

# Credentials and endpoint pulled from environment:
# ANVIL_S3_KEY=<access-key>
# ANVIL_S3_SECRET=<secret>
# ANVIL_S3_REGION=us-east-1
# ANVIL_S3_BUCKET=my-anvil-bucket
# ANVIL_S3_ENDPOINT=https://<r2-account-id>.r2.cloudflarestorage.com  # R2 / MinIO

Migration path

Switching from local to s3 later is a streaming migration: walk storage.objects, read each blob from the local backend, write it to S3 by the same hash, then flip the config. Because the database holds the canonical metadata, the path-level URLs stay identical for every client.