Storage Configuration
Configure S3-compatible object storage for your self-hosted Emberly instance — MinIO, AWS S3, Cloudflare R2, and more.
Emberly stores all uploaded files in S3-compatible object storage. You can use any provider that speaks the S3 API — including self-hosted options like MinIO. Storage is configured after first run via Admin Panel → Settings → Integrations → Storage.
Choosing a Provider
| Provider | Best for | Notes |
|---|---|---|
| MinIO (self-hosted) | Air-gapped or fully self-contained installs | Runs alongside the app, no cloud accounts needed |
| AWS S3 | Production with existing AWS infrastructure | Reliable, well-documented, generous free tier |
| Cloudflare R2 | Zero egress costs, global CDN | No egress fees; requires Cloudflare account |
| Vultr Object Storage | Cost-effective cloud storage | Managed via Admin Panel provisioning |
| Linode / Akamai | Linode-hosted apps | Managed via Admin Panel provisioning |
| Tigris | Globally distributed S3 | Good Fly.io companion |
MinIO (Self-Hosted)
MinIO is the easiest way to run a fully self-contained Emberly instance without any external dependencies.
Add MinIO to Docker Compose
Add this service to your docker-compose.yml:
Start MinIO:
Create a Bucket
Open the MinIO console at http://localhost:9001 and log in with your MINIO_ROOT_USER / MINIO_ROOT_PASSWORD. Then:
- Click Buckets → Create Bucket
- Name it
emberly - Leave other settings as default and click Create
Or use the mc CLI:
The last command makes the bucket publicly readable, which is required for Emberly's public file URLs to work.
Configure in Admin Panel
In Admin Panel → Settings → Integrations → Storage, set:
| Field | Value |
|---|---|
| Endpoint | http://minio:9000 (using Docker service name) |
| Bucket name | emberly |
| Region | us-east-1 (MinIO accepts any value) |
| Access key ID | minioadmin |
| Secret access key | minioadmin |
| Public files URL | http://localhost:9000/emberly |
Public files URL in production
For production use, Public files URL must be a publicly accessible URL — not localhost. Put MinIO behind a reverse proxy (Nginx or Caddy) on a real domain, e.g. https://files.yourdomain.com, and use that as the public URL.
MinIO Behind a Reverse Proxy
To serve MinIO files from a real domain, add a Caddy or Nginx block alongside your main app:
Caddy:
Nginx:
Then set Public files URL to https://files.yourdomain.com/emberly.
AWS S3
- Create a bucket in the S3 console
- Create an IAM user with
s3:GetObject,s3:PutObject,s3:DeleteObjectpermissions on the bucket - Generate an access key for that user
- In Admin Panel → Settings → Integrations → Storage:
| Field | Value |
|---|---|
| Endpoint | (leave blank — uses AWS default) |
| Bucket name | Your bucket name |
| Region | e.g. us-east-1 |
| Access key ID | IAM user access key |
| Secret access key | IAM user secret |
| Public files URL | https://your-bucket.s3.amazonaws.com or your CloudFront URL |
To allow public downloads, configure the bucket policy to allow s3:GetObject for all principals, or use a CloudFront distribution with the bucket as origin.
Cloudflare R2
R2 has no egress fees, making it cost-effective for high-traffic instances.
- Create a bucket in the Cloudflare dashboard under R2
- Create an R2 API token with Object Read and Write permissions
- Enable Public access on the bucket (or use a custom domain)
- In Admin Panel → Settings → Integrations → Storage:
| Field | Value |
|---|---|
| Endpoint | https://<account-id>.r2.cloudflarestorage.com |
| Bucket name | Your bucket name |
| Region | auto |
| Access key ID | R2 API token Access Key ID |
| Secret access key | R2 API token Secret |
| Public files URL | Your R2 public bucket URL or custom domain |
Verifying Storage Works
After configuring, upload a test file through the Emberly dashboard. If the file appears and its URL loads in a browser, storage is working correctly.
If uploads fail, check:
- The endpoint URL is correct and reachable from the app container
- The bucket exists and the access key has write permissions
- The
Public files URLis publicly accessible (not blocked by firewall or auth) - For MinIO: the bucket anonymous policy is set to
download
Related
- Production Deployment — full Docker Compose setup
- Environment Variables — what goes in
.envvs Admin Panel