New API Keys and Asymmetric Authentication
Configure new API keys and ES256 asymmetric authentication for self-hosted Supabase.
You can configure self-hosted Supabase to use the new API keys alongside the legacy API keys (ANON_KEY and SERVICE_ROLE_KEY HS256-signed JWTs).
Before you begin#
- Complete the Docker setup guide so that
JWT_SECRET,ANON_KEY, andSERVICE_ROLE_KEYare set in your.envfile. Quick start (Linux) handles this automatically; the manual path runsgenerate-keys.sh. - If you are upgrading from a legacy self-hosted Supabase environment, make sure to check the changelog and add/update the following files:
.env.example(merge new sections into your.envfile)docker-compose.ymlutils/add-new-auth-keys.shutils/rotate-new-api-keys.shvolumes/api/envoy/*
Adding the new keys#
From your project directory where you have docker-compose.yml:
1sh utils/add-new-auth-keys.sh --update-envThis generates new configuration environment variables and writes them to .env. Omit --update-env to review and confirm the changes interactively.
The script reads JWT_SECRET from .env and includes it as a symmetric key inside both JWT_KEYS and JWT_JWKS. If you later change JWT_SECRET, you must regenerate the JWKS as well.
In addition, the following configuration is uncommented automatically by the script for the new authentication to work correctly:
1auth:2 environment:3 # JSON array of signing JWKs (EC private + legacy symmetric)4 GOTRUE_JWT_KEYS: ${JWT_KEYS:-[]}56rest:7 environment:8 # PostgREST accepts a plain-text symmetric secret, a single JWK, or a JWKS.9 PGRST_JWT_SECRET: ${JWT_JWKS:-${JWT_SECRET}}1011realtime:12 environment:13 # JWKS for token verification (EC public + legacy symmetric)14 API_JWT_JWKS: ${JWT_JWKS:-{"keys":[]}}1516storage:17 environment:18 # JWKS for token verification (EC public + legacy symmetric)19 JWT_JWKS: ${JWT_JWKS:-{"keys":[]}}2021functions:22 environment:23 # JWKS for token verification (EC public + legacy symmetric).24 SUPABASE_JWKS: ${JWT_JWKS:-{"keys":[]}}Nested variable interpolation (${A:-${B}}) requires podman-compose >= 1.6.0. Earlier versions (still shipped by some Linux distributions) do not support it - if you are on an older podman-compose, either upgrade or replace each nested expression with the required variable directly, see the inline comments in docker-compose.yml for the exact substitutions.
Restart all services:
1sh run.sh recreateNew API keys format#
The new API keys use the same format as the Supabase platform:
1sb_publishable_<22-char-random>_<8-char-checksum>2sb_secret_<22-char-random>_<8-char-checksum>Verifying the setup#
Test with the new secret key:
1curl http://<your-domain>/rest/v1/ \2-H "apikey: your-supabase-secret-key"You should receive a valid response from PostgREST. Then verify that the legacy service role key still works:
1curl http://<your-domain>/rest/v1/ \2-H "apikey: your-service-role-key"Both should work and return the same result.
/rest/v1/ is the Open API root and requires admin-level keys (sb_secret_* or legacy SERVICE_ROLE_KEY). Public keys (sb_publishable_* or legacy ANON_KEY) return 403 for this endpoint.
You can also verify the public JWKS endpoint:
1curl http://<your-domain>/auth/v1/.well-known/jwks.jsonThis should return the EC public key (the symmetric key is excluded). Third-party services can use this endpoint to obtain the public key and verify asymmetric user session JWTs without needing the private key.
Environment variables configuration#
New variables default to empty values in .env.example. When empty, the API gateway and all services operate in legacy-only mode: sb_publishable and sb_secret API keys are not configured.
| Environment variable (existing and new) | Type | Description |
|---|---|---|
JWT_SECRET | Symmetric secret | Existing: Shared secret for signing and verifying HS256 JWTs. Used by multiple services. |
ANON_KEY | HS256 JWT | Existing: Legacy client-side API key. Embedded JWT with role: "anon". |
SERVICE_ROLE_KEY | HS256 JWT | Existing: Legacy server-side API key. Embedded JWT with role: "service_role". |
SUPABASE_PUBLISHABLE_KEY | Opaque | New: Short random key with checksum. Replaces ANON_KEY for client-side use. |
SUPABASE_SECRET_KEY | Opaque | New: Short random key with checksum. Replaces SERVICE_ROLE_KEY for server-side use. |
JWT_KEYS | JSON array | New: JSON array of signing JWKs containing the new asymmetric key pair and the legacy symmetric key. Used by Auth to sign tokens. |
JWT_JWKS | JWKS (JSON) | New: Contains the new public key and the legacy symmetric key. Used by PostgREST, Realtime, Storage, and Functions. |
Differences from the Supabase platform#
- One key per role. Self-hosted Supabase supports a single
sb_publishableand a singlesb_secret. The platform allows creating multiplesb_keys per project. - No checksum validation. The opaque keys use the same format as the platform (
sb_publishable_<random>_<checksum>), but the API gateway does not validate the checksum. Keys are matched as opaque strings by the API gateway.
Backward compatibility#
The new authentication configuration is fully backward compatible:
- The API Gateway accepts both key types simultaneously. You can migrate clients incrementally - some using legacy API keys, others using the new ones.
- JWKS includes the symmetric key.
JWT_JWKScontains both the EC public key (for verifying new ES256 tokens) and the legacyJWT_SECRETas a symmetric JWK (for verifying old HS256 tokens). Services that receiveJWT_JWKScan verify both token types. - No database changes required. The asymmetric key system operates entirely at the API gateway and service configuration layer.
When JWT_KEYS is set, Auth will start signing new user session JWTs with the new asymmetric ES256 key pair. Make sure all services that verify tokens (PostgREST, Realtime, Storage) are configured with JWT_JWKS so they can verify both the new ES256 and legacy HS256 tokens.
Rotating the new API keys#
If your new API keys are compromised or you want to rotate them periodically, you can regenerate sb_publishable and sb_secret without touching the asymmetric key pair:
1sh utils/rotate-new-api-keys.sh --update-envAfter rotating, restart services and update your client applications with the new keys:
1sh run.sh recreateRotating new API keys does not invalidate existing user sessions. User session JWTs issued by Auth are unaffected because they are verified using the asymmetric key pair, which remains unchanged.
Regenerating asymmetric key pair#
If the EC private key is compromised or you need to regenerate everything:
1sh utils/add-new-auth-keys.sh --update-envThis generates a new EC P-256 key pair, new JWKS, new asymmetric JWTs, and new sb_ API keys. After updating .env and restarting services:
- New user session tokens will be signed with the new EC key.
- Existing user session tokens signed with the old EC key will fail verification. Users will need to sign in again.
- Existing user session tokens signed with the legacy symmetric key (
JWT_SECRET) will continue to work, sinceJWT_SECREThasn't changed and is still included in the new JWKS.
Regenerating asymmetric keys invalidates all ES256 user sessions. Plan a maintenance window if your users have active sessions.
How it works#
Requests via supabase-js include two headers for every service except Edge Functions:
apikey- the API key (sb_or legacy JWT)Authorization- when unauthenticated, the client SDK copies the API key here (Bearer sb_publishable_xxxorBearer eyJ...). When authenticated, this contains the user session JWT minted by Auth.
For Realtime WebSocket connections, the API key is sent as a ?apikey= query parameter in the upgrade URL instead of an apikey header.
Storage and Edge Functions also accept requests without an API key. These services handle their own authentication.
For details on how the API gateway routes and authenticates requests, see the Envoy API Gateway guide.
Additional resources#
- Understanding API keys - How API keys work on the Supabase platform
- Auth architecture - How the Auth service handles authentication and token signing
- JWT Signing Keys - Best practices on managing keys used by Supabase Auth to create and verify JSON Web Tokens
- JSON Web Token (JWT) - How to best use JSON Web Tokens with Supabase
- Self-hosting with Docker - Initial setup guide, including legacy key generation
On GitHub: