EasyStarter logoEasyStarter
Object Storage

Alibaba Cloud OSS (Recommended for China)

Switch the EasyStarter storage provider to Alibaba Cloud OSS, reusing the RAM AccessKey

Alibaba Cloud OSS Storage

EasyStarter ships with Alibaba Cloud Object Storage Service (OSS) as a built-in storage provider. You can switch between OSS and the default Cloudflare R2 at any time. The server talks to OSS directly through the OSS REST API V4 with OSS4-HMAC-SHA256 signing — no Node.js SDK is required, so it runs natively on the Cloudflare Workers runtime.

If your product is mostly used inside mainland China, Alibaba Cloud OSS usually gives more stable latency and cheaper egress than Cloudflare R2. It also reuses the same RAM AccessKey as the Alibaba Cloud phone sign-in integration, which keeps operations simple.

ItemCurrent setup
Upload / download / list / deleteOSS REST API V4 with OSS4-HMAC-SHA256 signing
Server providerapps/server/src/storage/providers/aliyun-oss.ts
Provider registrationapps/server/src/storage/index.ts
Provider switchcommon.storage.provider in packages/app-config/src/app-config.ts
Public access path${SERVER_URL}/api/storage/aliyun-oss/<key> (proxied by the server; the bucket itself stays private)

The existing avatar and attachment upload types, MIME allowlists, and size limits are provider-agnostic. Switching to OSS does not require any change to business code.

Required Environment Variables

# Shared with the Alibaba Cloud phone sign-in integration
ALIBABA_CLOUD_ACCESS_KEY_ID=
ALIBABA_CLOUD_ACCESS_KEY_SECRET=

# OSS-specific
ALIYUN_OSS_BUCKET=
ALIYUN_OSS_REGION=
ALIYUN_OSS_ENDPOINT=

What each variable means:

VariableMeaningExample
ALIBABA_CLOUD_ACCESS_KEY_IDRAM user AccessKey ID, the long-term credential used by the serverLTAI5tXXXXXXXXXXXXXXXXX
ALIBABA_CLOUD_ACCESS_KEY_SECRETRAM user AccessKey Secret, shown only once on creationXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
ALIYUN_OSS_BUCKETOSS bucket name, without any domainyour-app-bucket
ALIYUN_OSS_REGIONRegion ID of the bucket, used as the region scope when signingcn-hangzhou
ALIYUN_OSS_ENDPOINTOSS access domain — must not include the bucket name or the protocoloss-cn-hangzhou.aliyuncs.com

ALIYUN_OSS_ENDPOINT must be the region-level public endpoint, e.g. oss-cn-hangzhou.aliyuncs.com. If you set it to your-app-bucket.oss-cn-hangzhou.aliyuncs.com, the provider will prepend the bucket again and produce an invalid host.

If you already configured ALIBABA_CLOUD_ACCESS_KEY_ID / ALIBABA_CLOUD_ACCESS_KEY_SECRET for phone sign-in, you can reuse the same AccessKey — just grant the existing RAM user additional OSS permissions.

Activate Object Storage Service

Make sure your Alibaba Cloud account has OSS activated and identity verification completed.

Product page: Alibaba Cloud Object Storage Service (OSS). Click Buy Now / Activate Now at the top of the page to enable the service.

Create an OSS bucket

Official docs: Create a bucket

  1. Log in to the OSS Console
  2. Click BucketsCreate Bucket
  3. Enter a Bucket name, e.g. your-app-bucket (globally unique, 3-63 characters, lowercase letters, digits, and hyphens only)
  4. Choose a Region, e.g. China (Hangzhou), which maps to the region ID cn-hangzhou
  5. Keep ACL as the default Private — files are served through a server-side proxy, the bucket does not need public access
  6. Accept defaults for the rest and click OK

Record the following:

  • Bucket name → ALIYUN_OSS_BUCKET
  • Region ID (the part after oss- in the bucket overview, e.g. cn-hangzhou in oss-cn-hangzhou) → ALIYUN_OSS_REGION
  • Public endpoint (the Endpoint (External) field on the bucket overview, e.g. oss-cn-hangzhou.aliyuncs.com) → ALIYUN_OSS_ENDPOINT

Grant the RAM user OSS permissions

Use a RAM user AccessKey instead of an Alibaba Cloud root account AccessKey. If you already created a RAM user for phone sign-in, simply attach an additional policy to the same user.

  1. Log in to the Alibaba Cloud RAM Console
  2. Go to IdentitiesUsers and select the target RAM user
  3. Open PermissionsGrant Permission
  4. Set Resource Scope to Account, search and check the system policy AliyunOSSFullAccess under Policy, then click OK to grant the permission

Select the AliyunOSSFullAccess system policy in the RAM grant-permission panel

This is the approach Alibaba Cloud officially recommends. Once the system policy is attached, the RAM user can read and write OSS objects.

Create or reuse the AccessKey

Official docs: Create an AccessKey pair

If you do not have an AccessKey yet:

  1. Open the Authentication or AccessKey tab on the RAM user detail page
  2. Click Create AccessKey and complete the security challenge
  3. Copy and save immediately:
    • AccessKey IDALIBABA_CLOUD_ACCESS_KEY_ID
    • AccessKey SecretALIBABA_CLOUD_ACCESS_KEY_SECRET

The AccessKey Secret is shown only once. If you lose it, you must disable the old AccessKey and create a new one.

If you already configured a RAM user AccessKey for phone sign-in, reuse it instead of creating another AccessKey for the same user.

Switch the storage provider

In packages/app-config/src/app-config.ts, change common.storage.provider from "r2" to "aliyun-oss":

packages/app-config/src/app-config.ts
storage: {
  enabled: true,
  provider: "aliyun-oss", // change from "r2" to "aliyun-oss"
  publicPath: "/api/storage",
  // ...rest unchanged
},

After this change, every avatar / attachment upload, download, list, and delete routes through the OSS provider automatically. Business code, upload components, and Better Auth avatar logic do not need to change.

Fill in local and production environment variables

For local development, add to apps/server/.dev.vars:

apps/server/.dev.vars
ALIBABA_CLOUD_ACCESS_KEY_ID=your-access-key-id
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your-access-key-secret

ALIYUN_OSS_BUCKET=your-oss-bucket
ALIYUN_OSS_REGION=your-oss-region
ALIYUN_OSS_ENDPOINT=your-oss-endpoint

For production deployment, add to apps/server/.env.production:

apps/server/.env.production
ALIBABA_CLOUD_ACCESS_KEY_ID=your-access-key-id
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your-access-key-secret

ALIYUN_OSS_BUCKET=your-oss-bucket
ALIYUN_OSS_REGION=your-oss-region
ALIYUN_OSS_ENDPOINT=your-oss-endpoint

After switching to OSS, R2_PUBLIC_URL is no longer needed and can be removed from both files. The r2_buckets binding (STORAGE) in apps/server/wrangler.jsonc is unused as well — you can keep it so you can switch back, or remove it.

Push secrets to production

Before deploying to Cloudflare Workers, push the secrets:

pnpm -F server secrets:bulk:production

After a successful push, the Worker runtime reads these values via env.ALIBABA_CLOUD_ACCESS_KEY_ID, env.ALIBABA_CLOUD_ACCESS_KEY_SECRET, env.ALIYUN_OSS_BUCKET, env.ALIYUN_OSS_REGION, and env.ALIYUN_OSS_ENDPOINT. Re-run this command whenever any of those values change — you do not need to redeploy code just because a secret changed.

Verify uploads locally

Start the server and the web client:

pnpm dev:server
pnpm dev:web

Sign in and upload an avatar from the profile page. The frontend posts the file to /api/storage/upload, the server calls the OSS provider's put method and writes the file under avatars/<userId>/..., and returns a public URL similar to:

http://localhost:3001/api/storage/aliyun-oss/avatars/<userId>/<uuid>.png

Reads are proxied back through /api/storage/aliyun-oss/<key>, so the bucket itself stays private.

You can confirm the object in the OSS Console Files view, and opening the public URL above in a browser should render the image.