Your self-hosted Supabase instance is only as secure as your API keys. If you're still using the legacy anon and service_role keys without a rotation strategy, you're one compromised credential away from a serious security incident. Worse, Supabase has announced that legacy API keys will be deprecated—making migration to the new key system not just recommended, but eventually mandatory.
This guide walks you through Supabase's new API key architecture, how to implement secure key rotation for self-hosted deployments, and the migration path from legacy keys. Whether you're running a single project or managing multiple instances, understanding API key security is fundamental to production operations.
Understanding Supabase's API Key Architecture
Supabase uses API keys to authenticate requests between your application and the various services (PostgREST, Auth, Storage, Realtime). The architecture has evolved significantly, and understanding both the legacy and new systems is critical for self-hosted operators.
Legacy API Keys (anon and service_role)
The original Supabase API keys are JSON Web Tokens (JWTs) signed with HS256:
- anon key: A low-privilege JWT that maps to the
anonPostgres role. Used for unauthenticated, public API access. - service_role key: A high-privilege JWT that bypasses Row Level Security. Used for server-side operations requiring elevated access.
These keys worked well initially but have significant limitations:
- 10-year expiry: Keys can't be rotated without breaking existing connections
- Tight coupling: The JWT secret, Postgres roles, and keys are interdependent
- No independent rotation: Changing one aspect affects everything
- No rollback capability: Failed rotations can cause extended downtime
New API Key System (sb_publishable and sb_secret)
Supabase introduced a new key format to address these issues:
sb_publishable_...: Replaces the anon key for client-side usesb_secret_...: Replaces the service_role key for server-side use
The key differences:
| Feature | Legacy Keys | New Keys |
|---|---|---|
| Format | JWT (HS256) | Opaque tokens |
| Rotation | Requires JWT secret change | Independent rotation |
| Session impact | Invalidates all sessions | Preserves user sessions |
| Multiple keys | One per type | Multiple secret keys possible |
| Signing | Symmetric (HS256) | Asymmetric (ES256) |
Setting Up New API Keys for Self-Hosted Supabase
If you're deploying a fresh Supabase instance, configuring the new key system from the start is straightforward. For existing deployments, you'll need to add new environment variables while maintaining backward compatibility.
Generating Cryptographic Keys
First, generate the EC P-256 key pair for asymmetric JWT signing:
# Generate EC private key openssl ecparam -name prime256v1 -genkey -noout -out ec_private.pem # Extract public key openssl ec -in ec_private.pem -pubout -out ec_public.pem # Convert to single-line format for .env EC_PRIVATE_KEY=$(cat ec_private.pem | tr '\n' '|') EC_PUBLIC_KEY=$(cat ec_public.pem | tr '\n' '|')
Generating API Keys
Generate the new-format API keys:
# Generate publishable key (replaces anon) PUBLISHABLE_KEY="sb_publishable_$(openssl rand -hex 24)" # Generate secret key (replaces service_role) SECRET_KEY="sb_secret_$(openssl rand -hex 24)" echo "PUBLISHABLE_KEY: $PUBLISHABLE_KEY" echo "SECRET_KEY: $SECRET_KEY"
Environment Configuration
Add these variables to your .env file alongside your existing configuration:
# New API Keys (coexist with legacy during migration) API_PUBLISHABLE_KEY=sb_publishable_xxx... API_SECRET_KEY=sb_secret_xxx... # EC Key Pair (use | as newline separator) EC_PRIVATE_KEY=-----BEGIN EC PRIVATE KEY-----|...|-----END EC PRIVATE KEY----- EC_PUBLIC_KEY=-----BEGIN PUBLIC KEY-----|...|-----END PUBLIC KEY----- # Legacy keys (keep during migration) ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... JWT_SECRET=your-jwt-secret
The system is backward compatible—both key types work simultaneously, allowing incremental client migration.
Implementing Key Rotation Procedures
Regular key rotation is essential for maintaining security. The new API key system makes this significantly easier because rotating keys doesn't invalidate user sessions.
Rotating New API Keys (No Downtime)
This is the preferred rotation method and can be done without service interruption:
# 1. Generate new keys NEW_PUBLISHABLE="sb_publishable_$(openssl rand -hex 24)" NEW_SECRET="sb_secret_$(openssl rand -hex 24)" # 2. Update .env file sed -i "s/API_PUBLISHABLE_KEY=.*/API_PUBLISHABLE_KEY=$NEW_PUBLISHABLE/" .env sed -i "s/API_SECRET_KEY=.*/API_SECRET_KEY=$NEW_SECRET/" .env # 3. Restart API gateway to load new keys docker compose restart kong # 4. Update your application configuration # (Client apps need the new publishable key)
The critical advantage: existing user sessions remain valid because the EC key pair used for signing user JWTs hasn't changed.
Rotating the EC Key Pair (Full Rotation)
If the EC private key is compromised, you need a complete key rotation:
# 1. Generate new EC key pair openssl ecparam -name prime256v1 -genkey -noout -out ec_private_new.pem openssl ec -in ec_private_new.pem -pubout -out ec_public_new.pem # 2. Generate new API keys NEW_PUBLISHABLE="sb_publishable_$(openssl rand -hex 24)" NEW_SECRET="sb_secret_$(openssl rand -hex 24)" # 3. Update .env with all new values # 4. Restart all services docker compose down && docker compose up -d
Important: Full EC key rotation invalidates all existing user sessions. Users will need to sign in again. Plan this for low-traffic periods and communicate with users if possible.
Rotation Schedule Recommendations
| Key Type | Rotation Frequency | Trigger Events |
|---|---|---|
| API Keys (sb_*) | Quarterly | Suspected exposure, team member departure |
| EC Key Pair | Annually | Confirmed compromise, compliance requirements |
| Legacy JWT Secret | Once (migrate away) | Moving to new key system |
Migrating from Legacy Keys
If you're running an existing self-hosted Supabase deployment, migrating to the new key system requires careful planning. The good news: Supabase designed this as a non-breaking, incremental migration.
Migration Timeline (Critical Dates)
Based on Supabase's announced timeline:
- Now until deprecation: Both legacy and new keys work simultaneously
- After deprecation: Legacy keys stop working; new keys required
Don't wait until the deadline. Start your migration now to avoid rushed changes.
Step-by-Step Migration Process
Phase 1: Add New Keys (No Client Changes)
- Generate EC key pair and new API keys as described above
- Add new environment variables to
.env - Restart services to load new configuration
- Verify the API gateway accepts both key types
# Test legacy key still works curl -H "apikey: $ANON_KEY" https://your-supabase.com/rest/v1/ # Test new key works curl -H "apikey: $PUBLISHABLE_KEY" https://your-supabase.com/rest/v1/
Phase 2: Update Client Applications
Update your client-side code to use the new publishable key:
// Before const supabase = createClient(url, process.env.SUPABASE_ANON_KEY) // After const supabase = createClient(url, process.env.SUPABASE_PUBLISHABLE_KEY)
For server-side code using service_role:
// Before const supabaseAdmin = createClient(url, process.env.SUPABASE_SERVICE_ROLE_KEY) // After const supabaseAdmin = createClient(url, process.env.SUPABASE_SECRET_KEY)
Phase 3: Validate and Remove Legacy Keys
After all clients are migrated:
- Monitor logs for any legacy key usage
- Remove
ANON_KEYandSERVICE_ROLE_KEYfrom.env - Remove
JWT_SECRETif no other services depend on it - Restart services to apply final configuration
Security Best Practices
Managing API keys securely requires more than just rotation. Here are practices specific to self-hosted Supabase:
Never Commit Keys to Version Control
This seems obvious, but it's the most common mistake:
# Add to .gitignore .env .env.* **/secrets/ *.pem
Use a secrets management solution like HashiCorp Vault or cloud provider secrets managers for production deployments.
Separate Keys Per Environment
Never use the same keys across development, staging, and production:
# .env.development API_PUBLISHABLE_KEY=sb_publishable_dev_xxx... API_SECRET_KEY=sb_secret_dev_xxx... # .env.production API_PUBLISHABLE_KEY=sb_publishable_prod_xxx... API_SECRET_KEY=sb_secret_prod_xxx...
Restrict Secret Key Access
The sb_secret_... key bypasses RLS and should only be used server-side:
- Never expose in client-side code or browser
- Store in server environment variables only
- Audit which services and team members have access
- Use least-privilege principles—not every backend service needs the secret key
Monitor Key Usage
Enable logging to detect anomalous API key usage:
-- Create audit table for API access patterns CREATE TABLE api_access_audit ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, key_type TEXT, endpoint TEXT, ip_address INET, user_agent TEXT, created_at TIMESTAMPTZ DEFAULT NOW() );
Integrate with your monitoring stack to alert on suspicious patterns.
Troubleshooting Common Issues
"Invalid API Key" After Rotation
If clients get authentication errors after key rotation:
- Verify the new key is correctly formatted (no trailing whitespace)
- Check that Kong has restarted and loaded the new configuration
- Confirm client applications are using the updated key
- Test directly with curl to isolate client vs. server issues
User Sessions Invalidated Unexpectedly
This happens when the EC key pair is rotated instead of just the API keys:
- For API key rotation, only regenerate
sb_publishableandsb_secret - Don't touch the EC private key unless necessary
- If sessions must be preserved, use the new API key rotation method
Legacy Key Still Required
Some older Supabase client libraries may not support the new key format. Check your client library version and upgrade if needed:
# Example: Update JavaScript client npm update @supabase/supabase-js@latest
Supascale Makes Key Management Easier
Managing API keys, rotation schedules, and migrations across multiple self-hosted Supabase projects quickly becomes complex. Supascale simplifies this with:
- Centralized configuration management for all your projects
- Environment variable management with secure storage
- Backup and restore that preserves your configuration including keys
- One-click deployment with proper key generation built-in
Instead of manually managing .env files and coordinating rotations across servers, Supascale provides a unified interface for managing multiple projects while maintaining security best practices.
Key Takeaways
- Migrate to new API keys now—legacy
anonandservice_rolekeys are being deprecated - The new system enables independent rotation—rotate API keys without invalidating user sessions
- Rotate quarterly at minimum—and immediately after any suspected exposure
- Never expose secret keys client-side—treat
sb_secret_...like a database password - Test rotations in staging first—a failed rotation in production causes downtime
API key security isn't glamorous, but it's foundational. A compromised service_role key gives attackers full access to your database, bypassing all your carefully crafted RLS policies. Take the time to implement proper key rotation procedures now—your future self (and your users) will thank you.
Ready to simplify your self-hosted Supabase management? Check out Supascale's features or get started with our documentation.
