Supabase Best Practices
- Leanware Editorial Team
- 10 minutes ago
- 9 min read
Supabase gives you Postgres, authentication, storage, and realtime subscriptions without managing infrastructure. It works well for MVPs and production apps, but moving from prototype to production requires you to follow certain practices.
In this guide, you’ll find practical ways to set up Supabase so it stays stable, secure, and maintainable, along with common mistakes you should avoid in production.
Supabase's Architecture & Shared Responsibility

Supabase runs on Postgres with GoTrue for auth, PostgREST for APIs, and Realtime for subscriptions. The platform handles server management, but you're responsible for schema design, Row-Level Security policies, backup configuration, and API key management.
This shared model means configuration mistakes become your problem. A missing RLS policy exposes your data. Poor indexing slows queries. No backup strategy means lost data during failures.
Common Pitfalls and Risks in Production
These issues compound in production, where fixing them becomes more complex and risky.
Skipping RLS during prototyping and forgetting to enable it before launch.
Exposing service_role keys in client code.
Not configuring backups until data loss occurs.
Making schema changes without proper migrations, causing inconsistencies across environments.
Using realtime subscriptions without filters, which can overload connections.
Security & Access Control
Row-Level Security: Enable & Optimize
RLS controls which rows users can access at the database level. Without it, anyone with your anon key can read all data.
Enable RLS on every table:
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
Write policies that match your access patterns:
CREATE POLICY "Users see own data"
ON users FOR SELECT
USING (auth.uid() = id);
Keep policies simple. Complex policies with multiple joins slow down queries. Test policies by connecting as different users and checking what data returns.
Role-Based Access Control & Policies
Supabase provides authenticated and anon roles by default. Create custom roles for specific access levels:
CREATE ROLE manager;
GRANT SELECT, UPDATE ON projects TO manager;
Combine roles with RLS for granular control. A user might have the manager role but RLS still limits them to their team's data.
Protecting API Keys & Secrets
Never use service_role keys in client code. They bypass RLS and grant full database access. Keep them server-side only.
Store keys in environment variables, not in code. Rotate keys periodically. Use different keys for each environment.
The anon key is safe for client use because RLS and policies protect your data. Still, monitor usage patterns to catch abuse.
Authentication & Password Best Practices
Use Supabase Auth instead of building your own. It handles password hashing, sessions, and JWT tokens correctly.
Require minimum password lengths. Enable email confirmation. Consider passwordless authentication with magic links for better security and user experience.
Rate limit authentication attempts to prevent brute force attacks. Supabase includes basic rate limiting, but add your own logic for sensitive operations.
Network, SSL & API Hardening
Supabase enforces TLS for all connections. Your client code should verify SSL certificates.
Add IP whitelisting if you need database access restricted to specific networks. Configure rate limits on your API endpoints. Set secure headers in your edge functions.
Schema Design & Data Modeling for Scale
Normalization vs Denormalization
Normalized schemas reduce redundancy but require joins. Denormalized schemas duplicate data for faster reads.
For Supabase, start normalized. Postgres handles joins well. Denormalize only when you hit performance issues and know your query patterns.
A users table referencing a separate addresses table makes sense. Duplicating user data in every order record usually doesn't.
Indexing, Partitioning & Query Optimization
Add indexes for columns you filter or join on:
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_created_at ON orders(created_at);
Use EXPLAIN to check query plans. Look for sequential scans on large tables. Those need indexes.
Partition large tables by date or other logical divisions. A logs table growing millions of rows monthly benefits from monthly partitions.
Handling Large Tables & Multi-Tenant Strategies
For multi-tenant apps, choose between schema-per-tenant or row-level isolation. Schema-per-tenant provides complete data separation but complicates management. Row-level with an organization_id column scales better for most cases.
Add an index on your tenant identifier. Write RLS policies that filter by it. This approach works until you need different schemas per tenant.
Realtime, Subscriptions & Edge Functions
Managing Realtime Subscriptions for Performance
Each subscription opens a connection. Creating subscriptions per row instead of per table kills performance.
Subscribe to tables with filters, not individual records:
supabase
.from('messages')
.on('INSERT', payload => {})
.filter('room_id', 'eq', roomId)
.subscribe()
Limit payload sizes. Don't send entire rows when you only need IDs. Unsubscribe when components unmount.
Use Cases & Considerations for Edge Functions
Edge functions run close to users for low latency. Use them for webhooks, API integrations, or processing that shouldn't happen client-side.
They're not ideal for long-running tasks. Keep execution under 50 seconds. For background jobs, use external services or database triggers.
Batching, Throttling & Filtering Events
Realtime can fire many events quickly. Debounce updates on the client side. Batch database writes to reduce connection overhead.
Filter events at the database level with RLS and WHERE clauses. Sending all events to the client for filtering wastes bandwidth.
CI/CD, Migrations & Schema Changes
Versioned Migrations & Rollback Strategies
Use the Supabase CLI to generate migrations:
supabase migration new add_user_roles
Write both up and down migrations. Test rollbacks before deploying. Keep migrations small and focused.
Store migrations in version control. Apply them in order across all environments. Document any manual steps required.
Managing Dev, Staging, Prod Environments
Run separate Supabase projects for each environment. Use the same schema across all. Store environment-specific configuration in separate files.
Never test migrations directly in production. Run them in staging first. Check for long-running operations that might lock tables.
Testing Policies, Functions & Changes Safely
Test RLS policies locally with the Supabase CLI. Create test users and verify they see the right data.
Write integration tests for edge functions. Mock external API calls. Test error handling, not just happy paths.
Monitoring, Logging & Performance
Query Analysis & EXPLAIN Plans
Run EXPLAIN ANALYZE on slow queries:
EXPLAIN ANALYZE
SELECT * FROM orders WHERE user_id = 'abc123';
Look at execution time and scan types. Sequential scans on large tables need indexes. High planning time suggests complex queries.
Metrics, Alerting & Dashboards
Supabase provides basic metrics. For production apps, export logs to external tools.
Track query duration, connection counts, and error rates. Set alerts for unusual patterns. Monitor disk usage and connection pool limits.
Audit Logs, Error Tracking & Anomalies
Log significant actions in an audit table. Include user_id, action type, and timestamp. Set retention policies to manage table size.
Track errors separately. Don't rely on application logs alone. Database-level logging catches issues your app might miss.
Backups, Recovery & Replication
Backup Strategies & Frequency
Supabase Pro plans include daily backups. Verify backups work by testing restores periodically.
Export critical data externally for redundancy. Automate exports with edge functions or external schedulers. Store backups in different regions.
Disaster Recovery Planning
Document recovery procedures. Know how long restoration takes. Test the process before you need it.
Identify critical tables and acceptable data loss windows. Some data can be recreated. User data can't.
Replication, Read Replicas & Failover
Supabase supports read replicas on higher tiers. Use them for reporting queries that don't need real-time data.
Replicas reduce load on your primary database. They don't provide automatic failover. Plan for manual intervention during outages.
Production Readiness Checklist
Before launching, verify these items:
RLS enabled on all tables
Backup strategy configured and tested
Service role keys secured server-side
Monitoring and alerts set up
Migration process documented
Rate limiting configured
SSL certificate verification enabled
Error tracking implemented
Compliance & Legal Considerations
Supabase offers data residency options for different regions. Choose locations that match your compliance requirements.
The platform provides encryption at rest and in transit. You're responsible for access controls and data handling policies.
For HIPAA or SOC 2, review Supabase's compliance documentation. Standard plans don't include full compliance. You may need additional measures.
Shared Responsibility Model
Supabase manages infrastructure. You manage access rules, schema design, backups, and compliance implementation.
Security vulnerabilities from misconfigured RLS are your responsibility. Performance issues from missing indexes are yours to fix. Know where the line sits.
Common Questions & Deep Dives
What if my RLS policies are too slow?
Check your policy complexity first. Policies that join multiple tables or run subqueries on every row create overhead.
Use EXPLAIN ANALYZE on queries with RLS enabled. Compare execution times with and without policies. The difference shows policy cost.
Simplify policies when possible. Cache computed values instead of calculating them in policies. Add indexes on columns your policies filter by. For complex access rules, move some checks to application level.
How do I debug RLS policies that aren't working?
Connect as the specific user having issues. Check what auth.uid() returns:
SELECT auth.uid();
Run the query that's failing. If it returns nothing, your policy isn't matching. Check the USING clause logic.
Temporarily disable RLS to verify data exists. If the query works without RLS, the policy logic is wrong. Review your conditions step by step.
Create test users and test data. Verify access works as expected in isolation before debugging production issues.
How do I handle Supabase downtime or outages?
Monitor Supabase’s status page for ongoing incidents. Most outages resolve quickly. Implement retry logic with exponential backoff and queue critical operations to retry after service restoration.
For critical apps, maintain read replicas or cached data for minimal functionality and document fallback procedures.
What breaks when I reach Supabase free tier limits?
The free tier provides:
500MB database size
5GB egress
1GB file storage
50,000 monthly active users
Shared CPU with 500MB RAM
Unlimited API requests
Community support
Projects are paused after 1 week of inactivity
Maximum of 2 active projects
When these limits are exceeded:
Writes fail if the database storage is full.
API requests may be throttled if usage exceeds allowed limits.
Free projects may be paused due to inactivity.
Monitor usage in the dashboard and upgrade to a Pro plan before hitting production traffic levels to avoid disruptions.
What if my database migrations fail in production?
Stop immediately and don’t try to fix forward blindly. Check migration logs for errors such as constraint violations, missing dependencies, or locked tables. If partially applied, assess the database state and perform manual cleanup if needed. Test rollback procedures in staging first, then execute in production, fix the migration script, and retry.
How do I optimize slow Supabase queries?
Start with EXPLAIN ANALYZE to locate bottlenecks like sequential scans or expensive operations. Add indexes on filtered columns and verify they’re used. Reduce data fetched by selecting only required columns, paginate large results, and watch for N+1 patterns, replacing repeated queries with joins or batch operations.
Supabase vs Firebase: When to choose which?
Firebase works best for mobile-first apps with simple data and offline sync. Supabase is better for relational data, complex queries, or teams familiar with SQL.
Firebase scales by operations, Supabase by compute and storage. Firebase offers more third-party integrations; Supabase gives direct database access and query flexibility.
RLS vs application-level security: When to use which?
RLS provides database-level protection, isolating users in multi-tenant setups. Application-level security handles rules based on workflow or external APIs. Most apps need both: RLS for baseline protection, application logic for business-specific rules.
How do I set up staging and production environments?
Create separate Supabase projects for each environment. Use the CLI to link projects locally:
supabase link --project-ref prod-project-id
Store connection details in environment files. Never commit production credentials to version control.
Keep schema migrations in git. Apply migrations to staging first, test thoroughly, then promote to production. Use the same migration files across all environments.
Configure CI/CD to run migrations automatically. Test in staging, then deploy to production after approval. Document the deployment process for your team.
How do I test my Supabase app locally?
Install Supabase CLI and Docker. Initialize a local project:
supabase init
supabase start
This spins up local Postgres, Auth, and Storage. Your app connects to localhost instead of production.
Create test data with SQL scripts. Run migrations locally before pushing to remote. Test RLS policies with different user contexts.
Stop local services with supabase stop. Everything runs in Docker containers that clean up completely.
How much does Supabase actually cost in production?
Supabase has tiered plans that scale with usage:
Free ($0/month): 500MB DB, 1GB storage, 5GB egress, 50,000 MAUs, max 2 projects, paused after 1 week.
Pro ($25/month base): 8GB DB, 100GB storage, 250GB egress, 100,000 MAUs included, daily backups, email support, upgradeable compute ($10/month base).
Team ($599/month): Pro features plus SSO, SOC2, optional HIPAA, priority support, extended backups/logs.
Enterprise (custom): Large-scale deployments with SLAs, dedicated support, BYO cloud, custom security.
Costs scale with database size, compute, storage, egress, and connections. Pro plan includes a spend cap to limit unexpected charges.
What's the maximum scale Supabase can handle?
Direct connections: 60 → 500 depending on tier
Pooled connections: up to 12,000
Database storage: up to 60TB
RAM: up to 256GB, CPU: up to 64 cores
Most apps reach performance/design limits (queries, indexing, schema) before hitting raw platform limits.
Your Next Move
Supabase handles the infrastructure so you can focus on building, but production readiness still requires discipline.
Enable Row-Level Security on all tables, test backups and recovery procedures, and monitor query performance. Document your deployment process and follow it consistently.
You can also connect with our team for guidance on setting up Supabase projects, optimizing performance, and ensuring production-ready deployments.