TurboJet
Storage

Connect to S3 storage

Configure S3-compatible credentials, CORS, and a private bucket.

All you need for storage is the S3_* environment variables and a private bucket. TurboJet talks to the provider through the AWS SDK with forcePathStyle: true, so any S3-compatible endpoint works.

Environment variables

Add these to the root .env (see Environment):

.env
S3_ACCESS_KEY_ID="your-access-key"
S3_SECRET_ACCESS_KEY="your-secret-key"
S3_ENDPOINT="https://your-endpoint"
S3_BUCKET="your-bucket"
S3_REGION="auto" # Optional; defaults to auto in the client
# S3_PUBLIC_URL="https://pub-….r2.dev" # Optional; for /api/storage/public
KeyRequiredNotes
S3_ACCESS_KEY_IDYesProvider access key
S3_SECRET_ACCESS_KEYYesProvider secret
S3_ENDPOINTYesS3 API endpoint
S3_BUCKETYesSingle bucket for all objects
S3_REGIONNoDefaults to auto
S3_PUBLIC_URLNoPublic base URL for GET /api/storage/public

Without the required keys, storage routes return 503 (Storage is not configured).

One bucket, namespaced paths

There is no packages/storage/config.ts with multiple bucket names. Everything lands in S3_BUCKET. The API namespaces objects as users/{userId}/{uuid}.{ext} so files stay isolated per user.

Keep the bucket private

Disable public access on the bucket. TurboJet issues short-lived presigned URLs for upload and download, and enforces ownership in the API. Only set S3_PUBLIC_URL if you intentionally expose objects via a public CDN or r2.dev URL.

CORS

Configure CORS on the bucket so browsers can upload from your marketing origin (NEXT_PUBLIC_MARKETING_URL). Typical allow list:

  • Methods: PUT, GET, HEAD (and POST/DELETE if your provider requires them)
  • Headers: include Content-Type
  • Origins: your production marketing URL and local marketing origin (for example http://localhost:3001)

Provider-specific steps:

Next steps

After env and CORS are set, follow Upload files to exercise the API.

On this page