Documentation menu

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.

UserOAuth popupprovider consenttoken exchangeserver-sidepick filesserver-side transferS3-compatiblestoragefile 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 Storagenot S3-compatible; createUpupHandler rejects 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.typeServiceS3 endpoint patternNotes
awsAmazon S3none — omit endpointThe only provider that needs no endpoint. Guide
r2Cloudflare R2https://<account-id>.r2.cloudflarestorage.comregion is the literal auto. Guide
minioMinIOyour own server URLSelf-hosted; requires path-style. Guide
backblazeBackblaze B2https://s3.<region>.backblazeb2.comRegion looks like us-west-004. Guide
digitaloceanDigitalOcean Spaceshttps://<region>.digitaloceanspaces.comRegion is the datacenter, e.g. nyc3. Guide
wasabiWasabihttps://s3.<region>.wasabisys.comGuide
gcsGoogle Cloud Storage (S3 interop)https://storage.googleapis.comOne global host; needs HMAC keys. Guide
supabaseSupabase Storagehttps://<project-ref>.storage.supabase.co/storage/v1/s3Path ends at /s3. Guide
hetznerHetzner Object Storagehttps://<location>.your-objectstorage.comLocation is fsn1, nbg1, or hel1. Guide
scalewayScaleway Object Storagehttps://s3.<region>.scw.cloudRegions incl. fr-par, nl-ams. Guide
linodeAkamai (Linode) Object Storagehttps://<region>.linodeobjects.comHost format changed in 2025 — copy it from the dashboard.
vultrVultr Object Storagehttps://<region>.vultrobjects.comRegion is the datacenter, e.g. ewr1.
upcloudUpCloud Object Storagehttps://<instance-id>.upcloudobjects.comEndpoint is per-instance — copy it from the console.
ovhcloudOVHcloud Object Storagehttps://s3.<region>.io.cloud.ovh.netRegions incl. gra, rbx, sgp.
alibabaAlibaba Cloud OSShttps://oss-<region>.aliyuncs.comOSS host, no s3. prefix. Rejects path-style.
oracleOracle Cloud Object Storagehttps://<namespace>.compat.objectstorage.<region>.oci.customer-oci.comSign with a Customer Secret Key, not your native OCI key.
contaboContabo Object Storagehttps://<region>.contabostorage.comRegions incl. eu2, usc1, sin1. Ceph-based, partial S3 coverage.
storjStorjhttps://gateway.storjshare.ioOne global gateway; region is global-1 on new projects.
idriveIDrive e2https://s3.<region>.idrivee2.comHost is provisioned per account — copy it from the dashboard.
cephCeph (RADOS Gateway)your own RADOS Gateway URLSelf-hosted.
azureAzure 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:

FieldTypeRequiredNotes
typeStorageProvider | stringYesProvider label from the table above. Also accepts any string for stores not listed.
bucketstringYesThe bucket (or "Space" / "container") name.
regionstringYesMust match the bucket's region. Use auto for Cloudflare R2.
accessKeyIdstringBoth or neitherOmit both to use the host's IAM role / instance profile (AWS). A half-set pair throws.
secretAccessKeystringBoth or neitherPairs with accessKeyId.
endpointstringNon-AWS onlyThe provider's S3 endpoint URL. Omit for native AWS S3.
forcePathStylebooleanOptionalDefaults 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 createUpupHandler walkthrough: OAuth, token store, tuning, and re-auth.
  • Quickstarts — copy-paste starting points for React, Vue, Svelte, Angular, Vanilla JS, Preact, and Next.js.