On this page

Storage providers

Which stores can hold a fleet's objects, and how each one is declared. The backend is a replaceable adapter behind the same stado:// object plane: the canonical layout, the queue and the object API answer the same on every one. stado capabilities object-storage prints this list from the binary's own catalog, and storage.backend takes the id or any alias in the first column.

Self-hosted

Backend What it is
stado (alias self-hosted) Your own machine and disk: a Stado object API that every host reaches, which also serves the S3 API. See self-host your storage.
minio A MinIO server you run.
garage A Garage cluster you run.
seaweedfs A SeaweedFS S3 gateway you run.
ceph (alias rgw) A Ceph Object Gateway you run.
local A directory on this machine only; a coordinate written here means nothing on another host.

Hosted

Backend Provider Declares
gcs Google Cloud Storage bucket
azure Azure Blob account, container
s3 Amazon S3 bucket, region (the host's AWS identity or the cloud-aws credential)
cloudflare (r2) Cloudflare R2 account id, bucket
backblaze (b2) Backblaze B2 region (us-west-004…), bucket
wasabi Wasabi region (us-east-1…), bucket
digitalocean (spaces) DigitalOcean Spaces region (nyc3…), bucket
hetzner Hetzner Object Storage location (fsn1…), bucket
scaleway Scaleway Object Storage region (fr-par…), bucket
ovhcloud (ovh) OVHcloud Object Storage region (gra…), bucket
akamai (linode) Akamai (Linode) Object Storage region, bucket
vultr Vultr Object Storage location (ewr1…), bucket
oracle (oci) OCI Object Storage, S3 compatibility API tenancy namespace as account, region, bucket
ibm (ibm-cos) IBM Cloud Object Storage region (us-south…), bucket
alibaba (aliyun, oss) Alibaba Cloud OSS region as OSS names it (oss-cn-hangzhou…), bucket
tencent (cos) Tencent Cloud COS region (ap-guangzhou…), bucket
huawei (obs) Huawei Cloud OBS region (cn-north-4…), bucket
exoscale (sos) Exoscale Simple Object Storage zone (ch-gva-2…), bucket
ionos IONOS Object Storage region, bucket
contabo Contabo Object Storage region (eu2…), bucket
upcloud UpCloud Managed Object Storage instance as account, region, bucket
storj Storj S3-compatible gateway region, bucket
filebase Filebase bucket
tigris Tigris bucket
yandex Yandex Object Storage bucket
idrive-e2 (idrive, e2) IDrive e2 the endpoint the account was issued, region, bucket
s3-compatible any other S3 API endpoint endpoint URL, region, addressing, bucket

Each named provider's endpoint is the one the provider publishes for its S3 API, built from what the table names; its addressing and, where the provider documents a single one, its signing region come with it. Every provider rated here is a preview until a release carries its live acceptance.

Stores that do not speak the S3 API

Not every object store has an S3 API, and Stado does not need one: gcs is reached through the Cloud Storage JSON API, whose writes are made conditional on an object's generation (ifGenerationMatch), and azure through the Blob service REST API, whose writes carry If-None-Match: * and If-Match: <etag>; local and stado are Stado's own. What every backend must offer is what the queue relies on: an if-absent create and a compare-and-swap replacement, so that a claim, a lease or a publication succeeds for one holder only.

Any other store needs a way to be one of these. If it publishes an S3 API (most do), it is s3-compatible with its endpoint, and the conditional-write proof below decides whether it may hold the queue. If it publishes none (an OpenStack Swift cluster without its S3 middleware, a file share), run an S3 gateway in front of it that keeps conditional writes, or keep the fleet's objects in a self-hosted stado store on a disk you own (self-hosting). A storage.backend the catalog does not name is refused when the store is opened, before anything is read or written, with WC_STORAGE_BACKEND=<name> is not supported (use "<every id>"); stado storage copy refuses one for --from/--to with unknown storage backend "<name>"; use one of: <every id>.

Declaring one

Every S3 API store reads the same storage.s3_compatible.* keys, each only where its provider asks for it, and none has a default:

Key Variable Read by
storage.s3_compatible.bucket WC_S3_COMPATIBLE_BUCKET every provider
storage.s3_compatible.credential_role WC_S3_COMPATIBLE_CREDENTIAL_ROLE every provider: the credential-store role whose item holds the key pair in access_key_id and secret_access_key
storage.s3_compatible.region WC_S3_COMPATIBLE_REGION every provider the table above lists with a region, location or zone, self-hosted servers and s3-compatible; the others sign for the one region their provider documents
storage.s3_compatible.account WC_S3_COMPATIBLE_ACCOUNT cloudflare, oracle, upcloud
storage.s3_compatible.endpoint WC_S3_COMPATIBLE_ENDPOINT self-hosted servers, idrive-e2 and s3-compatible
storage.s3_compatible.addressing WC_S3_COMPATIBLE_ADDRESSING s3-compatible only: path or virtual-hosted
stado config set storage.backend cloudflare
stado config set storage.s3_compatible.account example-account-id
stado config set storage.s3_compatible.bucket example-fleet
stado config set storage.s3_compatible.credential_role example-object-store

A disaster-recovery copy declares the same keys under storage.backup.s3_compatible.*, with its bucket in storage.backup.bucket. The key pair never enters the configuration: Stado reads the two fields from the selected credential store when it opens the store.

Refusals

Each refusal names what is missing and where it is declared:

  • the <provider> storage backend needs WC_S3_COMPATIBLE_<KEY> (storage.s3_compatible.<key>); nothing declares it
  • the <provider> storage backend (<product>) needs storage.s3_compatible.<key>; nothing declares it
  • the <provider> storage backend (<product>) runs where the deployment says: storage.s3_compatible.endpoint must name its URL
  • storage.s3_compatible.<key> "<value>" is not a single <product> name
  • WC_S3_COMPATIBLE_ENDPOINT (storage.s3_compatible.endpoint) "<value>" must be an absolute http:// or https:// URL
  • WC_S3_COMPATIBLE_ADDRESSING (storage.s3_compatible.addressing) is "<value>"; use "path" (the bucket in the path) or "virtual-hosted" (the bucket in the host name)
  • the credential playing role <role> holds no access_key_id; the s3-compatible backend reads its key pair from the access_key_id and secret_access_key fields

Conditional writes

The queue's claims, leases and release publications are if-absent creates (If-None-Match: *) and compare-and-swap replacements (If-Match). Amazon S3 documents both. Every other S3 API store proves them once, before the first write that relies on them: Stado creates one object under system/conditional-writes/<random>, creates it again, replaces it against a version it never held and removes it; both the second create and the replacement must be refused with 412. Reads, listings and plain writes never wait for the proof.

A store that stores either write is refused for every write that relies on them, for the life of the process, with the <provider> bucket <bucket> at <endpoint> ignores If-None-Match: * (…) and If-Match (…); the queue's claims, leases and publications would succeed for two holders at once. A check that could not finish (could not be checked for conditional writes: …, or honours conditional writes, but the proof object … could not be removed) decides nothing, and the next such write asks again.

Moving objects to or from one

stado storage copy and stado storage verify address any provider on either side: --from/--to name the backend, and --from-bucket, --from-account, --from-region, --from-endpoint, --from-addressing and --from-credential-role (and their --to- twins) carry what it declares:

stado storage copy --from local --from-path ~/.stado/local-storage \
  --to backblaze --to-region us-west-004 --to-bucket example-fleet \
  --to-credential-role example-object-store

Stado Desktop's Space panel offers the same fields under Copy objects between stores and Compare store names, metadata and bytes.

Machines a scheduler rents

An agent on a machine the scheduler rents opens the deployment's store with what its startup template carries: gcs, azure, s3, local, s3-compatible and every named provider, primary and backup. Before a machine is created the scheduler checks, per backend, that every binding the store needs is declared, and for an S3 API store that the agent's scoped vault grant (agent.skarbiec.roles) names its credential role, because the agent reads the key pair with that grant and nothing else. A missing binding is refused as agent startup setting <variable> is empty; configure <variable> (config key <key>), an ungranted role as … does not name this credential role, so an agent on a rented machine could not read the store's key pair.

A stado store (another Stado's object API) is refused for rented machines: its bearer is a file on the dispatching host that is never delivered to the machine. The same self-hosted store is reachable from one through its S3 API: declare s3-compatible with endpoint <object API>/s3, region stado, path addressing, and a credential role whose item holds the namespace as access_key_id and its bearer as secret_access_key. A backend name the catalog does not know is refused by name. Hosts that run their own Stado open every backend.

Source: this website