Supabase Setup
Supabase provides Krios's media storage by default. The provider abstraction means S3 / R2 / GCS work too — Supabase is just the easiest path for new deployments.
Create a Supabase project
- supabase.com → New project. Pick a region close to your users; matches your
KRIOS_STORAGE_REGIONenv var. - Wait for provisioning to complete.
Create a bucket
- Storage → New bucket.
- Name it
krios-media(or whatever you set inSUPABASE_STORAGE_BUCKET). - Visibility: Public is the simplest path for public delivery; keep it private if you need signed URLs for everything.
You can skip this step if you're fine with the defaults: Krios auto-creates the bucket as public on the first upload if it doesn't already exist. Create it manually when you want a private bucket or specific settings.
Get the service role key
Project settings → API → service_role key. Copy and set as SUPABASE_SERVICE_KEY. This key has full read/write on your project — server-side only, never expose to the client.
Set the URL
SUPABASE_URL is the project URL from the same page (e.g. https://abcdefg.supabase.co).
Image transforms
Supabase Storage's transform pipeline supports:
?width=…&height=…?resize=cover|contain?format=webp|avif|jpeg?quality=…
Krios's media URL builder maps the more ergonomic params (?w=, ?h=, ?fit=, ?fp=) to Supabase's underlying syntax. Enable image transforms in Storage → Settings in the Supabase dashboard.
Bucket-level policies
For public buckets, no RLS policy is needed — Krios reads via the service role.
For private buckets:
- Disable anonymous read.
- Krios signs URLs server-side at delivery time (
getSignedUrl(storageKey, 3600)for 1-hour-valid URLs). - The cache strategy: short TTL on signed URLs + edge caching by surrogate key.
Switching providers
The storage provider is plugged in at boot via src/lib/media/storage-provider.ts. To swap to S3:
import { S3StorageProvider } from "@/lib/media/storage-provider";
// In your boot file (or set via env-driven factory):
const provider = new S3StorageProvider({
bucket: process.env.S3_BUCKET!,
region: process.env.S3_REGION!,
accessKeyId: process.env.S3_ACCESS_KEY_ID!,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
});
Public-bucket reads work over the bucket's public URL. Private-bucket reads use signed URLs (getSignedObjectUrl).
Backups
Supabase Storage has built-in versioning. For larger deployments, replicate the bucket to S3 nightly via the Supabase CLI:
supabase storage cp --recursive sb://<project>/krios-media s3://backups/krios-media