Skip to content

Supabase Storage Buckets — File Upload and Management

DodaTech Updated 2026-06-28 5 min read

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

  1. Uploading without setting content-type: The Supabase SDK may default to octet-stream. Always set the correct MIME type for proper file serving.

  2. Not implementing storage RLS: Without storage RLS policies, any authenticated user could read or write any file in private buckets.

  3. Using public buckets for sensitive data: Public buckets are accessible without authentication. Never store sensitive files in public buckets.

  4. Signed URLs without expiration: Always set an expiration time for signed URLs. A signed URL with no expiration is equivalent to a public URL.

  5. Forgetting bucket size limits: Each bucket has a size limit. Monitor storage usage to prevent unexpected application failures.

Practice Questions

  1. 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.

  2. How do you secure files in a private bucket? Use storage RLS policies and signed URLs with expiration times.

  3. How do you generate a signed URL? Call supabase.storage.from_("bucket").createSignedUrl(path, expiry_seconds).

  4. What metadata can you add to a file upload? Custom metadata as key-value pairs, accessible in storage RLS policies via the metadata column.

  5. 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

Is Supabase Storage free?

The free tier includes 1 GB of storage. Paid plans increase storage limits.

What is the maximum file size?

The default maximum file size is 5 MB on the free tier and 50 MB on paid plans. Contact support for larger limits.

Can I use my own S3-compatible storage?

Supabase Storage uses S3 under the hood. Self-hosted Supabase can point to any S3-compatible service.

How do I delete a file?

Call supabase.storage.from_('bucket').remove(['path/to/file']).

Can I rename a file?

Supabase does not support renaming. Upload the file with the new name and delete the old one.

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