← All docs

Configuration & secrets

Environment variables and secrets — the three scopes, which one wins, and why a misplaced one fails silently.

Secrets & variables

  • •Add secrets (API keys, database URLs, tokens) under a project’s Secrets. They’re encrypted at rest and never shown in the UI or logs.
  • •For React/static apps, build-time variables are inlined during the build; for Node services they’re available to the running process.

Three scopes — and which one wins

  • •Every secret is stored at exactly one scope: workspace (tenant), project, or environment.
  • •Workspace — shared by every project you own. Project — every environment of that one project. Environment — that one environment (e.g. staging) only.
  • •They are merged when a deploy resolves its configuration, most specific first: environment overrides project, project overrides workspace. The same key at two scopes is not an error — the narrower one simply wins.
  • •Pick the narrowest scope that is still correct. Anything whose value DIFFERS per environment — OAuth callback URLs, API base URLs, webhook endpoints, any value naming a hostname — belongs at environment scope. A callback URL set at project scope points staging at production’s URL.

A secret on the wrong scope fails silently

  • •Nothing warns you. The secret exists, the console lists it, the deploy goes green — and the variable is still absent from the container, because it was attached to a different project or a different environment than the one you deployed.
  • •Symptom: the app crashes on boot, or one page returns 500 carrying the app’s own “missing/invalid environment variable” error, while the deployment shows as succeeded.
  • •Check what actually reaches the app, one scope at a time: curl -s -H "Authorization: Bearer $SHIPYARD_TOKEN" "$SHIPYARD_API/tenants/<t>/secrets?scopeType=environment&scopeId=<environmentId>" — then again with scopeType=project&scopeId=<projectId>, and scopeType=tenant. The union of those three is exactly what the app receives.
  • •Only key names come back. Values are write-only and can never be read through the API or the console, so verification is always by key, not by value.

Deleting a project does not carry its secrets forward

  • •Secrets belong to the project (or environment) they were created on. Delete a project and create a new one with the same name and you have a DIFFERENT project — none of the old secrets come with it, and the new one starts empty.
  • •The slug is what tells them apart. On delete the old project’s slug is freed for reuse by renaming it to <slug>~<id>, so the replacement takes the clean slug. Display names are not unique; ids and slugs are.
  • •So after recreating a project, re-add every variable — not just the ones you remember. List what the old app actually had before you delete it: GET /tenants/<t>/secrets?scopeType=project&scopeId=<oldProjectId>, and the same for each of its environments.
  • •Resolve the target from GET /tenants/<t>/projects and key off id and slug. Then GET /tenants/<t>/projects/<projectId> for environments[].id.
  • •Before writing a secret, confirm the environment id you are about to use is the one actually serving the URL you are fixing: GET /tenants/<t>/projects/<p>/environments/<e>/runtime returns that environment’s live URL. Match it against the URL in your browser — production and staging are easy to mix up, and a value on the wrong one is invisible.

Changes need a redeploy

  • •Variables are injected when the build runs and when the container starts — so adding, rotating or re-scoping a secret does NOT change anything already running.
  • •Deploy again after changing one. For Node/Python a rollback onto the same release is enough: it restarts the container with a freshly resolved environment and skips the rebuild.
  • •Set secrets BEFORE the first deploy of a new app, so the first build already has its build-time variables.

Setting them from the API

  • •POST $SHIPYARD_API/tenants/<t>/secrets with { "scopeType": "environment", "scopeId": "<environmentId>", "key": "SSO_CALLBACK_URL", "value": "…" }.
  • •scopeType is one of tenant | project | environment. scopeId is the project or environment id, and is omitted only for tenant scope.
  • •Rotate or rename an existing one: PATCH $SHIPYARD_API/tenants/<t>/secrets/<secretId> with { "value": "…" } or { "key": "NEW_NAME" }. Remove it: DELETE the same path.
  • •Keep your own copy of anything you cannot regenerate — the platform will not hand a value back to you.

Logs

  • •Watch logs live from a deployment’s detail page: build logs for every deploy, Node/Python runtime logs (container stdout/stderr) for server apps, and an Access log for Static/React apps (failed and non-GET requests, since static apps run no process).
  • •Secrets are automatically redacted from all log output. See “Deploy with the API” for the matching API endpoints and a troubleshooting reference.