Object Storage
Connect object storage, upload an avatar, and check your asset list.
Configure storage for avatar uploads and the account asset manager. Web uses S3-compatible storage and presigned URLs.
Before You Start
Have a running app and a test user. Prepare a bucket, API endpoint, scoped access credentials, and a public read URL for avatar previews. Keep development and production resources separate and credentials on the server.
1. Connect Storage
S3-Compatible Bucket
Create a bucket with your chosen S3-compatible provider. Obtain its endpoint and scoped credentials, then fill apps/web/.env.local:
S3_ENDPOINT="https://YOUR_S3_ENDPOINT"
S3_ACCESS_KEY_ID="YOUR_ACCESS_KEY"
S3_SECRET_ACCESS_KEY="YOUR_SECRET_KEY"
S3_REGION="auto"
S3_AVATARS_BUCKET="avatars"
S3_AVATARS_PROXY_URL="https://YOUR_PUBLIC_READ_BASE"Use the actual region required by your provider. S3_AVATARS_BUCKET must name your existing bucket; avatars is only the default. The read base is the URL prefix from which a relative object key can be read, not necessarily the S3 API endpoint.
The browser uploads directly using a signed URL. Configure bucket CORS to allow your development origin (http://localhost:3000) and production origin with the operations your upload flow uses. Configure public read access or your read proxy separately if using a public preview prefix.
2. Upload Your First Avatar
Restart the local app after configuration. Sign in, open /app/settings/general, select an image and complete the avatar crop/upload. Then open /app/settings/storage to inspect the file list and usage, download your own file, and delete a test asset you no longer need.
Check Your Result
- The uploaded avatar appears in your profile and after refresh.
- Your file is listed on the Storage settings page and can be downloaded.
- Deleting a test asset refreshes the list and usage.
- Another user cannot download or delete your session-protected assets.
Common Problems
| Problem | Fix |
|---|---|
503 STORAGE_ENV_MISSING on Node | Check all required S3 credentials and restart |
| Signed upload is blocked by the browser | Check bucket CORS and the exact local/production origin |
| Upload succeeds but preview is broken | Check the read prefix and object read permissions; an API endpoint is not always a public read URL |
Reference for Custom Uploads
src/server/storage.ts selects the app's driver. Asset APIs live in src/backend/api/routes/assets/, with UI under src/modules/settings/components/assets/.
| API | Purpose |
|---|---|
GET /api/assets | Paginated files: default 50, maximum 100 |
GET /api/assets/usage | Object count and bytes, aggregating up to 10,000 objects |
POST /api/assets/download-url | Signed download URL (900 seconds) |
POST /api/assets/delete | Up to 200 keys, with individual results |
Keys are scoped to the authenticated user. Preserve ownership validation and list/usage invalidation when extending these flows.