# Secrets

Store API keys, passwords, and other configuration as encrypted secrets that your app reads as environment variables.

## Two kinds of secrets

| Kind | Set with | Available | Creates a release |
|---|---|---|---|
| Runtime | `deplane secrets set NAME=VALUE` | As environment variables in every instance of the app. | Yes, if the app already has a release. |
| Build | `deplane secrets set --build NAME=VALUE` | Only while the app is built, for example a token for a private package registry. Never written into the built image. | No. The next `deplane deploy` uses them. |

Runtime and build secrets are separate. A name can exist in both, with different values.

## Set secrets

```sh
$ deplane secrets set DATABASE_URL=postgres://u:p@db.example.com/shop STRIPE_KEY=sk_live_123
Set runtime secret(s) DATABASE_URL, STRIPE_KEY.
Created release v5 with the new secrets.
```

You can set several secrets in one command. They all go into one new release, so your app restarts once.

```sh
$ deplane secrets set DATABASE_URL=postgres://u:p@db.example.com/shop --json
{
  "names": ["DATABASE_URL"],
  "scope": "runtime",
  "release": 5
}
```

`release` is `null` when no release was created. That happens for build secrets, and for apps that were never deployed: their first deploy picks the secrets up.

Set a build secret:

```sh
$ deplane secrets set --build NPM_TOKEN=npm_abc123
Set build secret(s) NPM_TOKEN.
Build secrets are used by the next build: run `deplane deploy`.
```

> Values typed on the command line can end up in your shell history. To avoid that, read them from a file or a variable, for example `deplane secrets set "DATABASE_URL=$(cat db-url.txt)"`.

## Remove secrets

```sh
$ deplane secrets unset STRIPE_KEY
Removed runtime secret(s) STRIPE_KEY.
Created release v6 without them.
```

Pass `--build` to remove build secrets. If a name is not set, nothing changes and the command fails with [secret_not_found](errors.md#secret_not_found).

## List secrets

`deplane secrets list` shows names, kinds, and when each changed. It never shows values.

```sh
$ deplane secrets list
NAME          SCOPE    UPDATED
NPM_TOKEN     build    2026-10-13 09:30
DATABASE_URL  runtime  2026-10-13 09:50
```

```sh
$ deplane secrets list --json
{
  "secrets": [
    {"name": "DATABASE_URL", "scope": "runtime", "updated_at": "2026-10-13T09:50:00Z"},
    {"name": "NPM_TOKEN", "scope": "build", "updated_at": "2026-10-13T09:30:00Z"}
  ]
}
```

## Names

- Names use capital letters, digits, and underscores: `A-Z`, `0-9`, and `_`.
- A name does not start with a digit.
- A name is at most 128 characters.
- `PORT` and names that start with `DEPLANE_` are reserved, because Deplane sets them.

`DATABASE_URL`, `STRIPE_KEY`, and `_INTERNAL_TOKEN` are valid. `database_url`, `1KEY`, and `MY-KEY` are not.

## Sizes

- One value can be up to 32 KB.
- All of an app's runtime secrets together can be up to 256 KB, and so can all of its build secrets.
- A value cannot contain a NUL byte. Encode binary values, for example as base64.

For larger files, store them elsewhere and put their location in a secret.

## How Deplane protects secret values

- Values are encrypted before they are stored. Each value has its own key, which is itself encrypted with a key kept outside the database.
- Values are never shown by the CLI or returned by the API. Once set, you can replace or remove a value, but not read it back.
- Values are never written to logs or to the audit log. The audit log records only the names that changed.
- `deplane --debug` replaces secret values with `[REDACTED]`.
- Build secrets are available only during the build step that needs them, and are never written into the image.

See [Security](security.md) for more.

## Limits

Secret limits are on the [Limits](limits.md#secrets) page.
