Supabase Storage Buckets — File Upload and Management
In this tutorial, you will learn about Supabase Storage Buckets. We cover key concepts, practical examples, and best practices to help you master this topic.
Supabase Storage provides S3-compatible file storage with integrated Row Level Security, allowing you to upload, serve, and manage files with the same security policies used for database tables.
What You'll Learn
By the end of this lesson you will create storage buckets, upload files via the SDK, set RLS policies on storage objects, generate public and signed URLs, and manage file lifecycle.
Why It Matters
File storage is a requirement for almost every application -- profile pictures, document uploads, media files. Supabase Storage integrates security directly into the storage layer using the same RLS policies you already know.
Real-World Use
DodaZIP stores user-uploaded ZIP files in a Supabase storage bucket. RLS policies ensure users can only access their own files. File access is controlled via signed URLs with expiration.
flowchart LR
U[User] -->|Upload file| API[Supabase Storage API]
API -->|RLS check| B[Storage Bucket]
B -->|Store| S[S3-compatible Backend]
U -->|Request file| API
API -->|Signed URL| U
style API fill:#3ecf8e,color:#fff
Creating Buckets
Create public and private storage buckets.
# create_buckets.py
# Create storage buckets
import os
from supabase import create_client, Client
url = os.getenv("SUPABASE_URL", "https://example.supabase.co")
key = os.getenv("SUPABASE_SERVICE_ROLE_KEY", "your-service-key")
supabase: Client = create_client(url, key)
def create_buckets():
# Create a public bucket for avatars
supabase.storage.create_bucket(
"avatars",
options={"public": True}
)
print("Created public bucket: avatars")
# Create a private bucket for user files
supabase.storage.create_bucket(
"user-files",
options={"public": False}
)
print("Created private bucket: user-files")
# Create a bucket for processing output
supabase.storage.create_bucket(
"processing-output",
options={"public": False}
)
print("Created private bucket: processing-output")
# List all buckets
buckets = supabase.storage.list_buckets()
for bucket in buckets:
print(f" Bucket: {bucket.name} (public: {bucket.public})")
create_buckets()
Uploading Files
Upload files with optional metadata and transformations.
# upload_files.py
# Upload files to Supabase Storage
def upload_file_example():
supabase = create_client(
os.getenv("SUPABASE_URL", "https://example.supabase.co"),
os.getenv("SUPABASE_ANON_KEY", "your-anon-key")
)
# Upload a file from bytes
file_bytes = b"ZIP file content would go here..."
response = supabase.storage.from_("user-files").upload(
"documents/report.zip",
file_bytes,
{"content-type": "application/zip"}
)
print(f"Upload response: {response}")
print(f"Path: documents/report.zip")
print(f"Size: {len(file_bytes)} bytes")
# Upload with custom metadata
metadata = {
"user_id": "user_abc123",
"original_name": "annual-report.pdf",
"upload_source": "web-app"
}
response = supabase.storage.from_("user-files").upload(
"reports/annual-report.pdf",
file_bytes,
{"content-type": "application/pdf", "metadata": metadata}
)
print(f"Uploaded with metadata: {response}")
upload_file_example()
Storage RLS Policies
Secure storage objects using SQL policies.
-- Enable RLS on storage.objects (enabled by default in new projects)
-- Allow users to read their own files
CREATE POLICY "Users can read own files"
ON storage.objects FOR SELECT
USING (auth.uid()::text = (metadata->>'user_id'));
-- Allow users to upload their own files
CREATE POLICY "Users can upload own files"
ON storage.objects FOR INSERT
WITH CHECK (auth.uid()::text = (metadata->>'user_id'));
-- Allow users to delete their own files
CREATE POLICY "Users can delete own files"
ON storage.objects FOR DELETE
USING (auth.uid()::text = (metadata->>'user_id'));
# storage_security.py
# Understanding storage RLS
def storage_rls_guide():
policies = [
"SELECT: Users can only see files they own",
"INSERT: Users can only upload files with their user_id in metadata",
"UPDATE: Users can only modify their own files",
"DELETE: Users can only delete their own files",
"Public bucket: Files are readable without authentication",
"Private bucket: All operations require proper RLS authorization",
]
print("Storage RLS Guidelines:")
for p in policies:
print(f" {p}")
storage_rls_guide()
Serving Files
Access files via public URLs or signed URLs.
# serve_files.py
# Generate file access URLs
def serve_files_example():
supabase = create_client(
os.getenv("SUPABASE_URL", "https://example.supabase.co"),
os.getenv("SUPABASE_ANON_KEY", "your-anon-key")
)
# Get public URL (for public buckets)
public_url = supabase.storage.from_("avatars").get_public_url(
"user_abc/avatar.png"
)
print(f"Public URL: {public_url}")
# Generate signed URL (for private buckets, expires in 1 hour)
signed_url = supabase.storage.from_("user-files").create_signed_url(
"documents/report.zip",
3600 # expiry in seconds
)
print(f"Signed URL: {signed_url['signedURL']}")
print(f"Expires in: 3600 seconds (1 hour)")
# Download a file
data = supabase.storage.from_("user-files").download(
"documents/report.zip"
)
print(f"Downloaded file: {len(data)} bytes")
serve_files_example()
Common Mistakes
Uploading without setting content-type: The Supabase SDK may default to octet-stream. Always set the correct MIME type for proper file serving.
Not implementing storage RLS: Without storage RLS policies, any authenticated user could read or write any file in private buckets.
Using public buckets for sensitive data: Public buckets are accessible without authentication. Never store sensitive files in public buckets.
Signed URLs without expiration: Always set an expiration time for signed URLs. A signed URL with no expiration is equivalent to a public URL.
Forgetting bucket size limits: Each bucket has a size limit. Monitor storage usage to prevent unexpected application failures.
Practice Questions
What is the difference between a public and private bucket? Public bucket files are accessible via public URLs without authentication. Private bucket files require signed URLs or RLS policies.
How do you secure files in a private bucket? Use storage RLS policies and signed URLs with expiration times.
How do you generate a signed URL? Call
supabase.storage.from_("bucket").createSignedUrl(path, expiry_seconds).What metadata can you add to a file upload? Custom metadata as key-value pairs, accessible in storage RLS policies via the metadata column.
Challenge: Create a file management system with upload, listing, public URL generation for images, and signed URL generation for documents with 24-hour expiration.
FAQ
Mini Project
Create a file upload system for a document management application with public and private buckets, RLS policies, signed URLs, and file listing.
def document_storage_system():
features = [
"Public bucket for profile images",
"Private bucket for user documents",
"RLS policies for user-owned files",
"Signed URLs with 1-hour expiry",
"File type validation (PDF, images only)",
"File size limit enforcement",
"File listing per user",
"File deletion with owner check",
]
print("Document Storage System:")
for feature in features:
print(f" [ ] {feature}")
document_storage_system()
What's Next
Next: Edge Functions for Serverless compute at the edge.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro