mirror of
https://github.com/supabase/supabase.git
synced 2026-09-22 13:37:53 +08:00
7012ba4a55
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.
61 lines
3.5 KiB
Plaintext
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)
|