API Key Rotation for Self-Hosted Supabase: A Complete Security Guide

Learn to manage Supabase's new API keys, rotate credentials securely, and migrate from legacy anon/service_role keys in self-hosted deployments.

Cover Image for API Key Rotation for Self-Hosted Supabase: A Complete Security Guide

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 anon Postgres 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:

  1. 10-year expiry: Keys can't be rotated without breaking existing connections
  2. Tight coupling: The JWT secret, Postgres roles, and keys are interdependent
  3. No independent rotation: Changing one aspect affects everything
  4. 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 use
  • sb_secret_...: Replaces the service_role key for server-side use

The key differences:

FeatureLegacy KeysNew Keys
FormatJWT (HS256)Opaque tokens
RotationRequires JWT secret changeIndependent rotation
Session impactInvalidates all sessionsPreserves user sessions
Multiple keysOne per typeMultiple secret keys possible
SigningSymmetric (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 TypeRotation FrequencyTrigger Events
API Keys (sb_*)QuarterlySuspected exposure, team member departure
EC Key PairAnnuallyConfirmed compromise, compliance requirements
Legacy JWT SecretOnce (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:

  1. Now until deprecation: Both legacy and new keys work simultaneously
  2. 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)

  1. Generate EC key pair and new API keys as described above
  2. Add new environment variables to .env
  3. Restart services to load new configuration
  4. 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:

  1. Monitor logs for any legacy key usage
  2. Remove ANON_KEY and SERVICE_ROLE_KEY from .env
  3. Remove JWT_SECRET if no other services depend on it
  4. 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:

  1. Verify the new key is correctly formatted (no trailing whitespace)
  2. Check that Kong has restarted and loaded the new configuration
  3. Confirm client applications are using the updated key
  4. 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_publishable and sb_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

  1. Migrate to new API keys now—legacy anon and service_role keys are being deprecated
  2. The new system enables independent rotation—rotate API keys without invalidating user sessions
  3. Rotate quarterly at minimum—and immediately after any suspected exposure
  4. Never expose secret keys client-side—treat sb_secret_... like a database password
  5. 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.


Further Reading