EmberlyEmberly Docs

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

ProviderBest forNotes
MinIO (self-hosted)Air-gapped or fully self-contained installsRuns alongside the app, no cloud accounts needed
AWS S3Production with existing AWS infrastructureReliable, well-documented, generous free tier
Cloudflare R2Zero egress costs, global CDNNo egress fees; requires Cloudflare account
Vultr Object StorageCost-effective cloud storageManaged via Admin Panel provisioning
Linode / AkamaiLinode-hosted appsManaged via Admin Panel provisioning
TigrisGlobally distributed S3Good 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:

services:
  minio:
    image: minio/minio:latest
    restart: unless-stopped
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin   # change this
    ports:
      - "9000:9000"   # S3 API
      - "9001:9001"   # Web console
    volumes:
      - minio_data:/data
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 10s
      timeout: 5s
      retries: 5
 
volumes:
  minio_data:

Start MinIO:

docker compose up -d minio

Create a Bucket

Open the MinIO console at http://localhost:9001 and log in with your MINIO_ROOT_USER / MINIO_ROOT_PASSWORD. Then:

  1. Click Buckets → Create Bucket
  2. Name it emberly
  3. Leave other settings as default and click Create

Or use the mc CLI:

docker compose exec minio mc alias set local http://localhost:9000 minioadmin minioadmin
docker compose exec minio mc mb local/emberly
docker compose exec minio mc anonymous set download local/emberly

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:

FieldValue
Endpointhttp://minio:9000 (using Docker service name)
Bucket nameemberly
Regionus-east-1 (MinIO accepts any value)
Access key IDminioadmin
Secret access keyminioadmin
Public files URLhttp://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:

files.yourdomain.com {
    reverse_proxy minio:9000
}

Nginx:

server {
    listen 80;
    server_name files.yourdomain.com;
 
    location / {
        proxy_pass http://minio:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        client_max_body_size 0;
    }
}

Then set Public files URL to https://files.yourdomain.com/emberly.


AWS S3

  1. Create a bucket in the S3 console
  2. Create an IAM user with s3:GetObject, s3:PutObject, s3:DeleteObject permissions on the bucket
  3. Generate an access key for that user
  4. In Admin Panel → Settings → Integrations → Storage:
FieldValue
Endpoint(leave blank — uses AWS default)
Bucket nameYour bucket name
Regione.g. us-east-1
Access key IDIAM user access key
Secret access keyIAM user secret
Public files URLhttps://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.

  1. Create a bucket in the Cloudflare dashboard under R2
  2. Create an R2 API token with Object Read and Write permissions
  3. Enable Public access on the bucket (or use a custom domain)
  4. In Admin Panel → Settings → Integrations → Storage:
FieldValue
Endpointhttps://<account-id>.r2.cloudflarestorage.com
Bucket nameYour bucket name
Regionauto
Access key IDR2 API token Access Key ID
Secret access keyR2 API token Secret
Public files URLYour 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 URL is publicly accessible (not blocked by firewall or auth)
  • For MinIO: the bucket anonymous policy is set to download

On this page