File Storage

File Storage Setup

Configure file storage - S3, Supabase Storage, Cloudflare R2, or MinIO

TL;DR: Set STORAGE_PROVIDER=s3 or supabase with credentials. Files upload directly to storage via presigned URLs.

Straktur uses presigned URLs for file uploads - files go directly from browser to storage, bypassing your server.

Supported Providers

ProviderBest ForPricing
Supabase StorageIf using Supabase alreadyFree tier included
AWS S3Production, high volumePay per use
Cloudflare R2No egress feesPay per use
MinIOSelf-hosted, developmentFree (self-hosted)

Supabase Storage

Recommended if you're already using Supabase for database.

Setup

  1. In Supabase dashboard, go to Storage
  2. Create a new bucket (e.g., files)
  3. Get your service key from Settings → API → service_role
STORAGE_PROVIDER=supabase
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIs...
SUPABASE_STORAGE_BUCKET=files

Use the service_role key, not the anon key. The service key bypasses Row Level Security.

Supabase Storage handles CORS automatically. No additional configuration needed.


AWS S3

Best for production - highly reliable, global CDN available.

Setup

  1. Create bucket in AWS S3 Console
  2. Create IAM user with S3 access
  3. Generate access keys
  4. Configure CORS (required for browser uploads)
STORAGE_PROVIDER=s3
S3_BUCKET=my-app-files
S3_REGION=us-east-1
S3_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
S3_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

IAM Policy (Minimum Required)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::my-app-files/*"
    }
  ]
}

CORS Configuration

In S3 Console → Bucket → Permissions → CORS:

[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["PUT", "GET"],
    "AllowedOrigins": ["http://localhost:3000", "https://yourdomain.com"],
    "ExposeHeaders": ["ETag"]
  }
]

Cloudflare R2

Best for cost - S3-compatible with zero egress fees.

Setup

  1. Create bucket in Cloudflare Dashboard → R2
  2. Create API token with R2 permissions
  3. Configure CORS (required for browser uploads)
STORAGE_PROVIDER=s3
S3_BUCKET=my-bucket
S3_REGION=auto
S3_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
S3_ACCESS_KEY_ID=your-access-key
S3_SECRET_ACCESS_KEY=your-secret-key

CORS Configuration

In Cloudflare Dashboard → R2 → Bucket → Settings → CORS Policy:

[
  {
    "AllowedOrigins": ["http://localhost:3000", "https://yourdomain.com"],
    "AllowedMethods": ["GET", "PUT"],
    "AllowedHeaders": ["*"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3600
  }
]

R2 uses region=auto. The endpoint URL contains your Cloudflare account ID.


MinIO

Best for development - S3-compatible, runs locally.

Docker Setup

Included in docker-compose.yml. Just run:

docker-compose up
STORAGE_PROVIDER=s3
S3_BUCKET=straktur-dev
S3_REGION=us-east-1
S3_ENDPOINT=http://minio:9000
S3_PUBLIC_ENDPOINT=http://localhost:9000
S3_ACCESS_KEY_ID=minioadmin
S3_SECRET_ACCESS_KEY=minioadmin

S3_ENDPOINT is for server-side (container network). S3_PUBLIC_ENDPOINT is for browser uploads (localhost).

CORS Configuration

CORS is pre-configured in docker-compose.yml. For manual setup via MinIO Console or CLI:

mc alias set myminio http://localhost:9000 minioadmin minioadmin
mc cors set myminio/straktur-dev --config cors.json
cors.json
{
  "CORSRules": [
    {
      "AllowedOrigins": ["http://localhost:3000"],
      "AllowedMethods": ["GET", "PUT"],
      "AllowedHeaders": ["*"],
      "ExposeHeaders": ["ETag"]
    }
  ]
}

Environment Variables

VariableRequiredDescription
STORAGE_PROVIDERYess3 or supabase
STORAGE_INTENT_SECRETRecommendedUpload token secret (min 32 chars)

For S3/R2/MinIO

VariableRequiredDescription
S3_BUCKETYesBucket name
S3_REGIONYesAWS region or auto for R2
S3_ACCESS_KEY_IDYesAccess key
S3_SECRET_ACCESS_KEYYesSecret key
S3_ENDPOINTFor R2/MinIOCustom endpoint URL
S3_PUBLIC_ENDPOINTFor DockerBrowser-accessible endpoint

For Supabase

VariableRequiredDescription
SUPABASE_URLYesProject URL
SUPABASE_SERVICE_KEYYesService role key
SUPABASE_STORAGE_BUCKETYesBucket name

How Uploads Work

┌─────────┐     1. Request URL      ┌─────────┐
│ Browser │ ──────────────────────► │ Server  │
└─────────┘                         └─────────┘
     │                                   │
     │                          2. Generate presigned URL
     │                                   │
     │      3. Presigned URL + Token     │
     │ ◄──────────────────────────────── │
     │                                   │
     │      4. Upload directly           │
     │ ─────────────────────────────►  ┌─────────┐
     │                                 │ Storage │
     │      5. Confirm upload          └─────────┘
     │ ──────────────────────────────►   │
     │                                   │
     │      6. File saved to DB          │
     │ ◄──────────────────────────────── │

Benefits:

  • No file size limits from serverless (bypasses 4.5MB Lambda limit)
  • Faster uploads (direct to storage)
  • Less server load

File Categories & Limits

CategoryMax SizeAllowed Types
PICTURES10 MBJPEG, PNG, WebP, GIF, SVG
AVATARS5 MBJPEG, PNG, WebP
DOCUMENTS25 MBPDF, DOC, DOCX, XLS, XLSX
THUMBNAILS10 MBJPEG, PNG, WebP, GIF, SVG

Common Issues

On this page