Secrets
Secrets are named, encrypted bags of key/values (AES-256-GCM at rest) that you reference from apps by name. The API never returns the values — only the key names. This is the right place for database URLs, API keys, tokens, etc. (better than inline env).
Two ways an app consumes a secret
| Mode | Field | Result in the container |
|---|---|---|
| Env vars | envFrom | each key becomes an environment variable |
| Files | secretFiles | each key is mounted read-only at /run/secrets/<KEY> (in RAM, never on disk) |
You can use either or both, at the app level (applies to all services) or per service.
Shared vs app-scoped
- Shared (org-wide): available to every app in your org.
- App-scoped: tied to one app by name. If a shared and an app-scoped secret share the same name, the app-scoped one wins for that app.
Create a secret
Dashboard
Secrets panel → name + key/value pairs → pick Scope (Shared or a specific app) → Save secret. Values are shown as password fields and never displayed again.
API
# shared (org-wide)
curl -s -X POST $API/secrets \
-H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{"name":"db-creds","data":{"DATABASE_URL":"postgres://u:p@host/db"}}'
# scoped to a specific app (by app name)
curl -s -X POST $API/secrets \
-H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{"name":"db-creds","data":{"DATABASE_URL":"postgres://…/app1"},"appId":"app1"}'
POST /secrets creates or replaces the secret of that name+scope.
Use it in a deploy
# inject as env vars + mount one key as a file
curl -s -X POST $API/deploy \
-H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{
"name":"api", "image":"acme/api:1.0", "port":3000,
"envFrom":["db-creds"],
"secretFiles":["tls-bundle"]
}'
envFrominjects every key ofdb-credsas environment variables.secretFilesmounts every key oftls-bundleat/run/secrets/<KEY>(read-only, RAM).
In the dashboard, the deploy form has Inject as env vars and Mount as files checkbox selectors listing your secrets.
Referencing a secret that doesn’t exist fails fast with
400 unknown_secretand the list of missing names.
Precedence (when keys collide)
Merged in this order, later wins (K8s envFrom semantics):
- app-level
envFromsecrets - per-service
envFromsecrets - inline
env(always wins)
And for any single name, an app-scoped secret beats a shared one.
List / delete
curl -s $API/secrets -H "authorization: Bearer $TOKEN" # names + key names only
curl -s -X DELETE "$API/secrets/db-creds" -H "authorization: Bearer $TOKEN" # shared
curl -s -X DELETE "$API/secrets/db-creds?appId=app1" -H "authorization: Bearer $TOKEN" # scoped
If deploys return 503 sealed
The platform can run with its master key held only in memory (“sealed”). When sealed, /deploy and /secrets return 503. An operator unseals it with POST /admin/unseal (see Troubleshooting). On the default setup this is automatic and you’ll never see it.
→ Use them when deploying: Deploy a Docker Image, Multi-Service Apps