Storage Providers
upup uploads to any S3-compatible object store. One uploader UI, one config
shape — the only thing that changes between AWS S3, Cloudflare R2, MinIO,
Backblaze B2, and the rest is the storage block you hand the server (or the
presign endpoint you point the client at).
There are two ways to wire storage. In client mode the browser uploads bytes
directly to storage using short-lived URLs your own presign endpoint signs — the
storage credentials never leave your server, and upup never sees them. In
server mode the browser talks only to your server and
@upupjs/server holds the credentials and writes to
storage for you. Which one to pick, and what changes on the wire, is covered in
Client Mode vs Server Mode.
This page focuses on server mode, where the storage provider is a
createUpupHandler config value. Cloud-drive sources land in that same storage:
after the user consents, the server exchanges the OAuth token and streams the
picked file straight into your bucket — the bytes never pass through the browser.
Pick your provider
Each guide below carries the full setup for one provider: the storage block,
where to create the bucket and keys in that provider's console, the CORS step,
and the mistakes that provider specifically invites.
- Amazon S3 — the native AWS path, including IAM roles instead of static keys.
- Cloudflare R2 — account-scoped endpoint,
region: 'auto', zero egress fees. - Backblaze B2 — application keys and the region baked into the endpoint host.
- DigitalOcean Spaces — Spaces keys and the datacenter-scoped endpoint.
- MinIO — self-hosted S3, plus the local Docker setup upup's own e2e suite uses.
- Azure Blob Storage — not S3-compatible;
createUpupHandlerrejects it, so use the client-mode SAS path. - Other S3-compatible services — Wasabi, Google Cloud Storage, Supabase, Hetzner, Scaleway, Storj, and every other store that speaks the S3 API, including ones upup has never heard of.
Supported providers
@upupjs/core exports a StorageProvider enum with the values below. Every one
except azure exposes an S3-compatible API and is served by @upupjs/server;
azure is the sole exception.
storage.type | Service | S3 endpoint pattern | Notes |
|---|---|---|---|
aws | Amazon S3 | none — omit endpoint | The only provider that needs no endpoint. Guide |
r2 | Cloudflare R2 | https://<account-id>.r2.cloudflarestorage.com | region is the literal auto. Guide |
minio | MinIO | your own server URL | Self-hosted; requires path-style. Guide |
backblaze | Backblaze B2 | https://s3.<region>.backblazeb2.com | Region looks like us-west-004. Guide |
digitalocean | DigitalOcean Spaces | https://<region>.digitaloceanspaces.com | Region is the datacenter, e.g. nyc3. Guide |
wasabi | Wasabi | https://s3.<region>.wasabisys.com | Guide |
gcs | Google Cloud Storage (S3 interop) | https://storage.googleapis.com | One global host; needs HMAC keys. Guide |
supabase | Supabase Storage | https://<project-ref>.storage.supabase.co/storage/v1/s3 | Path ends at /s3. Guide |
hetzner | Hetzner Object Storage | https://<location>.your-objectstorage.com | Location is fsn1, nbg1, or hel1. Guide |
scaleway | Scaleway Object Storage | https://s3.<region>.scw.cloud | Regions incl. fr-par, nl-ams. Guide |
linode | Akamai (Linode) Object Storage | https://<region>.linodeobjects.com | Host format changed in 2025 — copy it from the dashboard. |
vultr | Vultr Object Storage | https://<region>.vultrobjects.com | Region is the datacenter, e.g. ewr1. |
upcloud | UpCloud Object Storage | https://<instance-id>.upcloudobjects.com | Endpoint is per-instance — copy it from the console. |
ovhcloud | OVHcloud Object Storage | https://s3.<region>.io.cloud.ovh.net | Regions incl. gra, rbx, sgp. |
alibaba | Alibaba Cloud OSS | https://oss-<region>.aliyuncs.com | OSS host, no s3. prefix. Rejects path-style. |
oracle | Oracle Cloud Object Storage | https://<namespace>.compat.objectstorage.<region>.oci.customer-oci.com | Sign with a Customer Secret Key, not your native OCI key. |
contabo | Contabo Object Storage | https://<region>.contabostorage.com | Regions incl. eu2, usc1, sin1. Ceph-based, partial S3 coverage. |
storj | Storj | https://gateway.storjshare.io | One global gateway; region is global-1 on new projects. |
idrive | IDrive e2 | https://s3.<region>.idrivee2.com | Host is provisioned per account — copy it from the dashboard. |
ceph | Ceph (RADOS Gateway) | your own RADOS Gateway URL | Self-hosted. |
azure | Azure Blob Storage | — (no S3 API) | Not servable — throws at construct time. Guide |
Placeholders in angle brackets come from your provider's console. Where a pattern is marked "copy it from the dashboard", the host is provisioned per account or per instance and cannot be derived from your region — read the exact value off the console rather than assembling it.
The type value is a label. @upupjs/server builds the same AWS-SDK S3 client
for every S3-compatible value and reaches your backend through endpoint — it
does not branch on type. Because storage.type also accepts any string, a
store that isn't in the enum works too, as long as it speaks the S3 API: use the
generic recipe.
azure is the one value with no S3 surface. createUpupHandler throws an
UpupConfigError at construct time if you pass it, rather than failing later at
request time. See Azure Blob Storage.
The storage config
Every provider guide fills in the same object:
| Field | Type | Required | Notes |
|---|---|---|---|
type | StorageProvider | string | Yes | Provider label from the table above. Also accepts any string for stores not listed. |
bucket | string | Yes | The bucket (or "Space" / "container") name. |
region | string | Yes | Must match the bucket's region. Use auto for Cloudflare R2. |
accessKeyId | string | Both or neither | Omit both to use the host's IAM role / instance profile (AWS). A half-set pair throws. |
secretAccessKey | string | Both or neither | Pairs with accessKeyId. |
endpoint | string | Non-AWS only | The provider's S3 endpoint URL. Omit for native AWS S3. |
forcePathStyle | boolean | Optional | Defaults to true when endpoint is set (MinIO requires it); ignored for native AWS S3. |
bucket, region, and — when set — a complete accessKeyId/secretAccessKey
pair are validated at construct time, so a forgotten env var fails loudly on boot
instead of surfacing as a confusing 500 later.
The AWS S3 guide shows the complete
createUpupHandler call; every other guide shows only the storage block that
changes, since uploadTokenSecret and the rest of the handler are identical. See
the Server Mode setup guide for the full
walkthrough (getUserId, providers, tokenStore, and tuning).
Never hard-code credentials
Placeholders in every guide are environment variables. Access keys and secrets belong in server-side environment variables, never in a file you commit and never in anything the browser can fetch. In client mode the browser sees only the short-lived signed URL your endpoint returns.
Troubleshooting
SignatureDoesNotMatch / 403 on upload. Almost always a wrong region or a
skewed server clock. Make region match the bucket's actual region (use auto
for R2), and confirm the server clock is accurate — S3 signatures are
time-sensitive and tolerate only a few minutes of drift. A half-set credential
pair (one of accessKeyId/secretAccessKey blank, the classic
process.env.X! → "" bug) throws at construct time; set both or neither.
Path-style vs virtual-hosted addressing. upup enables path-style
automatically whenever endpoint is set, which is what MinIO and most
S3-compatible stores expect. If your provider only supports virtual-hosted-style
and you see hostname or TLS errors, set forcePathStyle: false. This flag is
ignored for native AWS S3.
Bucket or endpoint errors (NoSuchBucket, DNS/TLS failures). Check that
bucket exists in that region, and that the region segment inside endpoint
matches region.
CORS (client mode only). When the browser uploads directly to storage, the
bucket must allow your app's origin for the signed PUT (and its headers). In
server mode the browser only talks to your server, so bucket CORS isn't part of
the upload path. See Credentials And CORS.
Next steps
- Server Mode — Setup — the full
createUpupHandlerwalkthrough: OAuth, token store, tuning, and re-auth. - Quickstarts — copy-paste starting points for React, Vue, Svelte, Angular, Vanilla JS, Preact, and Next.js.