> ## Documentation Index
> Fetch the complete documentation index at: https://docs.slipway.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Secrets & variables

> Two stores for the values your spec references — encrypted secrets and plaintext variables.

Your spec references values with `${...}`; slipway resolves them at deploy time. There are two stores:

* **Secrets** — encrypted at rest. Slipway never returns a secret value after you create it; it only appears inside your running container. Use them for API tokens, database URLs, signing keys.
* **Variables** — plaintext. Visible to anyone with viewer role or above. Use them for log levels, feature flags, registry hosts, build identifiers.

A missing reference fails the deploy before any container starts.

## Scopes

Both stores have two scopes:

| Scope            | Where you set it                                                       | Strict reference                         |
| ---------------- | ---------------------------------------------------------------------- | ---------------------------------------- |
| **Environment**  | An environment's settings (or the composer's Variables & Secrets step) | `${secret.env.NAME}` / `${var.env.NAME}` |
| **Organization** | **Settings → Secrets** / **Settings → Variables**                      | `${secret.org.NAME}` / `${var.org.NAME}` |

The bare shorthand — `${secret.NAME}` / `${var.NAME}` — resolves **environment → org**, first match wins. Use the shorthand when you want fallback; use the explicit `env` / `org` form when you want a strict lookup that never falls through.

Environment values are defined once on the environment and apply to **every deployment of it** — the permanent instance, PR previews, and manual/dev instances all resolve the environment's *current* values on each deploy. There are no per-instance overrides: editing variables & secrets from an instance's settings edits the environment's, and every deployment picks them up on its next redeploy (see the **Stale** chip).

## Substitution syntax

```yaml theme={"system"}
services:
  api:
    build:
      args:
        NPM_TOKEN: ${secret.NPM_TOKEN}        # secret in build.args
        REGISTRY:  ${var.org.REGISTRY}        # explicit org variable
    env:
      DATABASE_URL: ${secret.DATABASE_URL}     # env first, org fallback
      STRIPE_KEY:   ${secret.env.STRIPE_KEY}   # env only — strict
      DATADOG_KEY:  ${secret.org.DATADOG_KEY}  # org only — strict
      LOG_LEVEL:    ${var.LOG_LEVEL}           # variable, env first, org fallback
```

| Form                                     | Resolves to                                                                                                |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `${secret.NAME}` / `${var.NAME}`         | env store first, org store as fallback                                                                     |
| `${secret.env.NAME}` / `${var.env.NAME}` | environment store only — fails if missing                                                                  |
| `${secret.org.NAME}` / `${var.org.NAME}` | org store only — fails if missing                                                                          |
| `${service.<name>.<field>}`              | a sibling service's URL / host / port — see [composition](/environments/overview#talking-between-services) |

Names match `^[A-Z][A-Z0-9_]*$`.

## Hardcoded values are authoritative

A key with a plain literal value in the spec (`NODE_ENV: production`, `PGHOST: postgres`) — or a computed `${service.X}` value — isn't a reference. **The spec value is what deploys, always.** A variable or secret of the same name never overrides it, so the running config can't silently drift from what the repo says.

To let a value vary per environment, make it a reference instead of a literal:

```yaml theme={"system"}
env:
  NODE_ENV: ${var.NODE_ENV}   # now set per-environment (or org-wide) in the stores
```

That's the single, explicit way to say "this is overridable." The environment composer lists every hardcoded/computed key read-only under **Defined in spec** so you can see what the spec already handles, but there's nothing to set there.

## How values reach your container

Resolved `env` values are injected into your container's environment at startup. They never appear in the pod definition, the deployment row, or anywhere a viewer would see them. Resolved `build.args` are passed to the build as build arguments only, never on the build command line.

<Warning>
  A `build.args` value your `Dockerfile` then writes to disk (e.g. `RUN echo $TOKEN > /etc/secret`) gets baked into the image layer. Slipway can't prevent that — keep credentials out of build args unless your `Dockerfile` consumes them in the same `RUN` step.
</Warning>

## Setting values

Manage org-wide values under **Settings → Secrets** and **Settings → Variables**. Manage environment-scoped values on the environment's settings or in its composer. To add one: click **Add** in the top right, give it an uppercase name and a value, and save. A secret's value is masked and can't be read back — to rotate, update it.

<Frame caption="Settings → Secrets manages org-wide secrets. Environment-scoped secrets live on each environment.">
  <img src="https://mintcdn.com/slipway/XyuXtxn4cFnSJANr/images/settings/org-secrets.png?fit=max&auto=format&n=XyuXtxn4cFnSJANr&q=85&s=9a2ac425bb622bf0b4633f2ec4efd56b" alt="Org secrets page" width="1920" height="1200" data-path="images/settings/org-secrets.png" />
</Frame>

### The Secrets Manager

A repo's **Spec** tab has a Secrets Manager below the rendered spec. It reads your effective spec the way a deploy does, lists every `${secret.*}` / `${var.*}` it references, and shows each as **set** (with the scope it resolves from) or **missing**. Click **Set** on any row to provide the value inline — handy for getting a new repo fully configured before its first deploy.

### Per-service provenance

An environment's **Components** tab is the read-only companion. Expand a service's **env** to see every variable it receives, each tagged with where it resolves from — `spec` for a hardcoded literal, `env` for an environment value, `org` for an org value — plus an `unset` flag for any reference nothing satisfies yet. Secret values stay masked; variable and literal values are shown inline. Use the **edit** links to jump to the environment or org stores when a value needs changing.

## What's logged

Every deploy records what it resolved:

* `secrets_resolved` lists `{name, scope, source}` per reference — **never values**.
* `vars_resolved` lists `{name, scope, source, value}` — values included, because variables aren't secret.

If you want a value kept out of the event log, make it a secret.

## Permissions

| Role                      | Read secret names | Read secret values | Read variables | Create / edit / delete |
| ------------------------- | ----------------- | ------------------ | -------------- | ---------------------- |
| Viewer                    | yes               | no                 | yes            | no                     |
| Developer / Admin / Owner | yes               | no                 | yes            | yes                    |

No role can read a secret's value once it's set — secrets are write-only after creation.
