Sitepins needs an S3-compatible bucket. Before configuring one, it’s worth being clear about what actually goes in it.
Two Different Kinds of Media
| Kind | Examples | Where it’s stored |
|---|---|---|
| Site content media | Images in blog posts, anything in your media library | Your Git repository |
| Application images | User avatars, organization thumbnails, project thumbnails | The S3 bucket |
Your site’s media is committed to Git like everything else — that’s the whole point of a Git-based CMS. The bucket holds Sitepins’ own interface images, which don’t belong in anyone’s repository.
So: if the bucket is misconfigured, avatars and thumbnails break. Your content and its images keep working.
Supported Providers
Anything S3-compatible:
- AWS S3
- Cloudflare R2
- MinIO — self-hosted
- Backblaze B2
- DigitalOcean Spaces
Setup
1. Create a bucket
Create a bucket with your provider and generate an access key and secret.
2. Configure the API
In api/.env:
S3_ENDPOINT="https://s3.us-east-1.amazonaws.com"
S3_REGION="us-east-1"
S3_ACCESS_KEY="<access key>"
S3_SECRET_KEY="<secret key>"
S3_BUCKET_NAME="<your bucket name>"
S3_FORCE_PATH_STYLE=false
Provider notes:
- Cloudflare R2 — set
S3_REGION="auto" - MinIO and other path-style gateways — set
S3_FORCE_PATH_STYLE=true - DigitalOcean Spaces — use the Spaces endpoint for your region
3. Configure the app
In app/.env, set the bucket’s public base URL:
NEXT_PUBLIC_BUCKET_URL="https://<bucket>.s3.<region>.amazonaws.com"
This must correspond to the same bucket as the API’s S3_* values, or uploads will succeed and then fail to display.
4. Set CORS on the bucket
Allow your app’s origin in the bucket’s CORS configuration — http://localhost:3000 in development, your real domain in production. Without it, browser uploads are blocked.
Legacy DigitalOcean Variables
Existing DigitalOcean Spaces setups keep working. DOS_PUBLIC_ACCESS_KEY, DOS_PUBLIC_SECRET_KEY, DOS_BUCKET_NAME, and DOS_REGION are still honored and derive the endpoint automatically. New installs should use the S3_* variables.
Verifying It Works
Upload a profile picture at Account → Display Picture. If it uploads and displays, the bucket is configured correctly at both ends.
If it uploads but shows as broken, NEXT_PUBLIC_BUCKET_URL is wrong or the bucket isn’t publicly readable. If the upload itself fails, check the API’s S3_* values and the bucket’s CORS rules.
Content Media Configuration
Your site’s media folders are configured per project, inside the app, not through environment variables. See Configure Your Site and Media Library.