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):
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| Key | Required | Notes |
|---|---|---|
S3_ACCESS_KEY_ID | Yes | Provider access key |
S3_SECRET_ACCESS_KEY | Yes | Provider secret |
S3_ENDPOINT | Yes | S3 API endpoint |
S3_BUCKET | Yes | Single bucket for all objects |
S3_REGION | No | Defaults to auto |
S3_PUBLIC_URL | No | Public 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(andPOST/DELETEif 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.