Supabase Setup Guide
Learn how to configure Boards to use Supabase as your database, storage, and authentication provider instead of the default local PostgreSQL setup.
Why Use Supabase?
Supabase provides a complete backend-as-a-service that includes:
- PostgreSQL Database - Managed PostgreSQL with connection pooling
- Storage - S3-compatible object storage for generated artifacts
- Authentication - Built-in auth with multiple providers
- Realtime - WebSocket subscriptions for live updates
- Row Level Security - Perfect for multi-tenant applications
This guide shows you how to migrate from the local Docker PostgreSQL setup to Supabase.
Prerequisites
- Supabase account (free tier available)
- Existing Boards development environment
- Basic understanding of PostgreSQL
1. Create Supabase Project
- Sign up at supabase.com
- Create a new project:
- Choose a project name (e.g., "boards-dev")
- Set a database password (save this!)
- Select a region close to you
- Wait for setup (usually 1-2 minutes)
2. Get Connection Details
From your Supabase project dashboard:
- Go to Settings → Database
- Find the Connection string section
- Copy the connection details:
# Direct connection (for development)
postgresql://postgres.[PROJECT_REF]:[PASSWORD]@db.[PROJECT_REF].supabase.co:5432/postgres
# Connection pooling (for production)
postgresql://postgres.[PROJECT_REF]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres?pgbouncer=true
- Go to Settings → API and copy:
- Project URL
- Anonymous public key (
anonkey) - Service role key (
service_rolekey) - Keep this secret!
3. Update Environment Configuration
Create or update your environment file:
# packages/backend/.env.supabase
# Database Configuration
BOARDS_DATABASE_URL=postgresql://postgres.abcdefghij:your_password@db.abcdefghij.supabase.co:5432/postgres
# Supabase API Configuration
SUPABASE_URL=https://abcdefghij.supabase.co
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# For production, use connection pooling:
# BOARDS_DATABASE_URL=postgresql://postgres.abcdefghij:your_password@aws-0-us-east-1.pooler.supabase.com:6543/postgres?pgbouncer=true
Security Note: Never commit the service role key to version control. Use environment variables or secure secret management.
4. Apply Database Schema
Apply your schema to Supabase using Alembic:
cd packages/backend
uv run alembic upgrade head
This will create all tables and required extensions (e.g., uuid-ossp).
5. Configure Storage
Set up Supabase Storage for generated artifacts:
Create Storage Buckets
In the Supabase dashboard:
- Go to Storage
- Create buckets:
generations- For AI-generated contentthumbnails- For preview imageslora-models- For custom models (if applicable)
Set Bucket Policies
-- Allow authenticated users to upload to their tenant's folder
CREATE POLICY "Users can upload to their tenant folder" ON storage.objects
FOR INSERT TO authenticated
WITH CHECK (
bucket_id = 'generations'
AND (storage.foldername(name))[1] = auth.jwt() ->> 'tenant_id'
);
-- Allow users to read their tenant's files
CREATE POLICY "Users can view their tenant files" ON storage.objects
FOR SELECT TO authenticated
USING (
bucket_id = 'generations'
AND (storage.foldername(name))[1] = auth.jwt() ->> 'tenant_id'
);
Update Backend Configuration
# packages/backend/src/boards/config.py
import os
from supabase import create_client
# Supabase client for storage operations
SUPABASE_URL = os.environ.get('SUPABASE_URL')
SUPABASE_KEY = os.environ.get('SUPABASE_SERVICE_KEY') # Use service key for backend
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
# Storage configuration
STORAGE_BUCKET = 'generations'
THUMBNAIL_BUCKET = 'thumbnails'
6. Row Level Security (RLS)
Enable RLS for multi-tenant data isolation using Alembic revisions (see Database Migrations guide for patterns). Example:
from alembic import op
def upgrade() -> None:
op.execute("ALTER TABLE users ENABLE ROW LEVEL SECURITY;")
# ... create policies with IF NOT EXISTS guards ...
def downgrade() -> None:
# ... drop policies if exist, then disable RLS ...
op.execute("ALTER TABLE users DISABLE ROW LEVEL SECURITY;")
7. Authentication Integration
Configure Supabase Auth to work with your existing user system:
Update User Registration
# When creating users, sync with Supabase Auth
from supabase import create_client
async def create_user(email: str, password: str, tenant_id: str):
# Create user in Supabase Auth
auth_response = supabase.auth.sign_up({
"email": email,
"password": password,
"options": {
"data": {
"tenant_id": tenant_id # Custom claim for RLS
}
}
})
# Create user record in your database
user_record = await create_user_record(
auth_subject=auth_response.user.id,
auth_provider="supabase",
email=email,
tenant_id=tenant_id
)
return user_record
JWT Custom Claims
Ensure the JWT includes tenant_id for RLS policies:
-- Function to add tenant_id to JWT
CREATE OR REPLACE FUNCTION public.custom_access_token_hook(event jsonb)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
BEGIN
-- Add tenant_id from user metadata to JWT
IF event->>'user_id' IS NOT NULL THEN
DECLARE tenant_id TEXT;
SELECT u.tenant_id INTO tenant_id
FROM public.users u
WHERE u.auth_subject = (event->>'user_id')
AND u.auth_provider = 'supabase';
IF tenant_id IS NOT NULL THEN
event := jsonb_set(event, '{tenant_id}', to_jsonb(tenant_id), true);
END IF;
END IF;
RETURN event;
END;
$$;
-- Grant permissions
GRANT EXECUTE ON FUNCTION public.custom_access_token_hook TO service_role;
8. Local Development Options
Option 1: Direct Remote Connection
Use your Supabase project directly for development:
# Use remote database URL
export BOARDS_DATABASE_URL="postgresql://postgres.xxx:password@db.xxx.supabase.co:5432/postgres"
# Start development
make dev
Pros: Real cloud environment, no local setup needed
Cons: Requires internet connection, shared with team
Option 2: Supabase CLI Local Development
Run Supabase locally while staying in sync with remote:
# Start local Supabase
supabase start
# This provides local URLs:
# Database: postgresql://postgres:postgres@localhost:54322/postgres
# API URL: http://localhost:54321
# Sync schema from remote
supabase db pull
# Make local changes and push
supabase db push
Pros: Offline development, isolated environment
Cons: Additional setup, need to sync changes
9. Production Configuration
Connection Pooling
For production, use connection pooling to handle high load:
# Use pooled connection string
BOARDS_DATABASE_URL=postgresql://postgres.xxx:password@aws-0-region.pooler.supabase.com:6543/postgres?pgbouncer=true
Performance Optimization
-- Add indexes for common queries (if not already present)
CREATE INDEX CONCURRENTLY idx_generations_tenant_status ON generations(tenant_id, status);
CREATE INDEX CONCURRENTLY idx_boards_tenant_owner ON boards(tenant_id, owner_id);
-- Optimize for time-series queries
CREATE INDEX CONCURRENTLY idx_generations_created_at ON generations(created_at DESC);
Monitoring and Alerts
Set up monitoring in Supabase dashboard:
- Database tab → Monitor connection count, query performance
- API tab → Monitor request volume and errors
- Storage tab → Monitor storage usage and bandwidth
10. Testing Your Setup
Verify everything works:
# Test database connection
psql $BOARDS_DATABASE_URL -c "SELECT version();"
# Test your application
cd packages/backend
python -c "from boards.dbmodels import Users, Boards; print('✅ import ok')"
# Start the development server
make dev
Next Steps
With Supabase configured, you can now:
- Deploy to production using the same Supabase project
- Set up Supabase Storage for file uploads and serving
- Configure Supabase Auth for user authentication
- Use Supabase Realtime for live updates
- Set up Edge Functions for webhooks and background jobs
For detailed migration workflows, see the Database Migrations guide.