Storage: S3-compatible files, with the same RLS-style access control as tables
AWS's own domain taught S3 as the standard for object storage, with its own separate access-control system (IAM policies, bucket policies). Supabase Storage is built on the same underlying object-storage model, but makes one deliberate, distinctive choice: reuse RLS, the exact same access-control mechanism already covering your tables.
Intermediate
2 min read
The underlying model: objects in buckets, same as S3
Supabase Storage organizes files into buckets (a top-level namespace) containing objects (the actual files, each with a path) — the same conceptual model AWS's own S3 lesson covers directly, and genuinely S3-API-compatible at the protocol level, meaning existing tooling built for S3 often works against Supabase Storage with minimal changes.
The distinctive choice: access control via RLS policies, not a separate bucket-policy language
CREATE POLICY "users can only upload to their own folder"ON storage.objects FOR INSERTWITH CHECK ( bucket_id = 'avatars' AND (storage.foldername(name))[1] = auth.uid()::text);
AWS's own S3 lesson covers bucket policies and IAM policies as S3's own, separate access-control language — genuinely powerful, but a distinct system from whatever access-control mechanism governs your actual database tables. Supabase Storage instead represents every object as a row in its own internal storage.objects table, meaning the exact same RLS mechanism lesson 2 already covered governs file access too — a policy checking auth.uid() against a path's folder name is the same pattern, the same policy language, the same mental model as every other RLS policy already covered in this domain, rather than a second, unrelated access-control system to learn.
Public vs. private buckets: the one genuinely separate setting
Public bucket: any object's URL is directly fetchable, no auth check at all —
right for truly public assets (a site's logo, public avatars)
Private bucket: every access goes through RLS policies, exactly like a table
A bucket's public/private setting is a genuine, separate switch — a public bucket skips RLS entirely for reads, trading access control for simplicity and CDN-friendliness, appropriate specifically for content that's meant to be universally readable. A private bucket keeps every read and write subject to the same RLS policy evaluation as lesson 2's tables, appropriate for anything genuinely sensitive or user-specific (a private document, an unpublished draft's attached image).
Transformations: resizing images on the fly, without a separate image-processing pipeline
GET /storage/v1/render/image/public/avatars/user-123/profile.png?width=200&height=200
Supabase Storage can resize an image on request, cached after the first transformation — a genuinely convenient built-in feature that avoids needing a separate image-processing service or pre-generating every size variant upfront, though for heavy, specialized image-processing needs beyond simple resizing, a dedicated service is still the more appropriate tool.