Skip to content

External Secrets Injection

This page explains how a stored external secret reaches a running app's environment, and how to verify it.

Mechanics

At deploy time the control-plane resolves the secrets that apply to the app (app-specific overrides + inherited tenant defaults, app wins per provider+key), decrypts them, and materializes a Kubernetes Secret named app-{app_id}-external-secrets in the app's namespace. The app's Deployment references that Secret via envFrom (optional: true), so each provider key becomes an environment variable in the pod — and if the app has no external secrets, the reference is a harmless no-op.

secret stored (encrypted)
        │  deploy
control-plane: resolve + decrypt + Patch::Apply
K8s Secret  app-{app_id}-external-secrets
        ▼  envFrom (optional: true)
pod env:  OPENAI_API_KEY=…

The injection is best-effort: a missing master key or empty secret set never fails the user-visible deploy — the pod simply starts without those vars.

Set it up

  1. Add the credential — tenant-wide in Settings → External Secrets, or app-specific on the app's External Secrets tab.
  2. Deploy the app (POST /v1/apps/{id}/deploys, or push/redeploy as usual).
  3. The new pod boots with the provider keys in its environment.

Deploy API contract

The injection runs as part of the deploy. POST /v1/apps/{id}/deploys must include tenant_id (and app_id) in the request body — the injection hook resolves the secrets from body.tenant_id:

{
  "app_id":    "<app-uuid>",
  "tenant_id": "<tenant-slug>",
  "image_ref": "registry/image:tag"
}

Empty tenant_id silently skips injection

A deploy whose body omits tenant_id (e.g. only image_ref) resolves secrets for an empty tenant → zero rows, no Secret materialised, no external_secret.injected event. The deploy itself still succeeds (the pod just starts without the provider keys). Always send tenant_id. The dashboard and CLI populate it for you; only hand-crafted API calls are at risk.

Verify

# inspect the materialized Secret
kubectl -n paas-apps get secret app-<app_id>-external-secrets -o yaml

# confirm the var is present in the running pod
kubectl -n paas-apps exec deploy/<app> -- env | grep OPENAI_API_KEY

A successful injection emits an external_secret.injected audit event (paas_audit_events), carrying the app id and the number of keys injected.

Troubleshooting

Symptom Likely cause
var absent from pod env no external secret set for the tenant/app, or the deploy predates adding it → redeploy
Secret object missing injection skipped (no secrets, or master key unset) — check control-plane logs
wrong value injected an app override exists — check the app's External Secrets tab (app wins over tenant default)