Files
supabase__supabase/apps/docs/content/troubleshooting/working-around-the-edge-function-secrets-limit.mdx
Miranda Limonczenko 7012ba4a55 docs(functions): regroup the secrets guide by information type (#50419)
Moves and heading levels only. No claims changed.

Studio renders a Docs button at
apps/studio/pages/project/[ref]/functions/secrets.tsx:43 pointing at
#using-the-cli, but "Using the CLI" was bold text rather than a heading, so the
anchor had no target and the button dropped the reader at the top of the page.
It and "Using the Dashboard" are now real headings, which repairs it.

Local secrets and Production secrets were h3 under "Accessing environment
variables", but neither is about accessing one. Both are now h2 siblings, and
the reference list moved to the end, so the page runs procedures first and facts
last.

Sections are ordered by what the reader is doing, not by subject: set a secret
locally, read it in code, then set it in production. "Accessing environment
variables" sat after production, which put the reading step after the shipping
step.

Local secrets held a two-item list of the loading mechanisms, which is a fact
sitting inside a procedure. It is now the section's opening sentence, where a
one-line fact can qualify the procedure without interrupting it.

Every existing heading text is unchanged, so #default-secrets, #local-secrets,
#production-secrets and #accessing-environment-variables all still resolve.

Added a value statement opener, and an outcome after the production procedure.
No intro outline: the page is short and its headings already scan.

The frontmatter title was title case. Renaming it to sentence case moves a
navigation label and a search entry, so the nav entry and the three pages that
used the old title as link text change with it. The slug is untouched.
2026-09-18 15:55:32 -07:00

61 lines
3.5 KiB
Plaintext

---
title = "Working around the Edge Function secrets limit"
topics = [ "functions", "cli" ]
keywords = [ "secrets", "environment variables", "api keys", "limit", "json" ]
[api]
cli = [ "supabase-secrets-set", "supabase-secrets-list" ]
---
Edge Functions projects are capped at **100 secrets** (see [Functions limits](/docs/guides/functions/limits#secrets)). If you're managing credentials for many third-party services, you can hit this cap even though you haven't done anything wrong — each `supabase secrets set NAME=VALUE` call uses one slot, regardless of how small the value is.
Supabase doesn't raise this limit on a per-project basis today. The workaround below gets around it — though the per-secret size limit below still applies, so it isn't a way around every constraint.
## How to fix
1. Group related credentials into a JSON object and store it under a single secret name:
```bash
supabase secrets set API_KEYS='{"stripe":"sk_live_xxx","sendgrid":"SG.xxx","aws":"xxx"}'
```
This counts as **one** secret against the 100 limit, no matter how many keys are nested inside it.
2. Read it back in your function with `Deno.env.get()` and `JSON.parse`:
```typescript
const raw = Deno.env.get('API_KEYS')
if (!raw) {
throw new Error('Missing API_KEYS secret')
}
const keys = JSON.parse(raw)
const stripeKey = keys.stripe
```
3. Confirm it was set with `supabase secrets list`.
This is the same pattern Supabase's own default secrets use — `SUPABASE_PUBLISHABLE_KEYS` and `SUPABASE_SECRET_KEYS` are themselves JSON dictionaries parsed the same way (see [Environment variables](/docs/guides/functions/secrets#accessing-environment-variables)).
### Things to watch for
- **No partial updates.** Setting a new value replaces the whole secret, so updating one key means re-setting the entire JSON blob. Build the JSON with a script or `jq` rather than hand-typing it — a single typo breaks every key in the group.
- **Quoting on the command line gets messy fast**, especially outside bash/zsh (e.g. Windows shells don't handle single-quote literals the same way). If that happens, load the secret from a file instead — the value isn't parsed by the shell, which sidesteps the escaping problem:
```bash
supabase secrets set --env-file .env
```
- **Secrets are capped at 48 KiB (24,576 characters)** ([Functions limits](/docs/guides/functions/limits#secrets)). Group logically — for example, one secret per third-party integration — rather than merging everything into a single blob. Typical API keys (tens to a couple hundred characters) leave plenty of headroom; long-lived tokens, JWTs, or certificates can approach the size cap with only a handful of entries.
## When this isn't the right fit
Consider [Supabase Vault](/docs/guides/database/vault) when your values vary by row, such as secrets tied to individual users or organizations. Vault stores encrypted secrets in Postgres, and the 100-secret limit doesn't apply.
Reading a Vault secret from an Edge Function costs a database round-trip, either `supabase.rpc()` against a `security definer` function or a service-role query. That adds latency.
For a flat list of project-wide credentials, [grouping them into a single JSON secret](#how-to-fix) avoids the round-trip.
## Additional resources
- [Functions limits — Secrets](/docs/guides/functions/limits#secrets)
- [Environment variables — accessing and setting secrets](/docs/guides/functions/secrets)
- [Supabase Vault](/docs/guides/database/vault)