---
sidebar_position: 9
question: Where does config resolve secrets in each deploy mode?
kind: task
via_web: available
via_cli: available
via_python: available
via_api: available
via_config: available
ref_web: System › Data › Secrets
ref_cli: abi secrets naas push-env-as-base64
ref_python: secret.set
ref_api: PUT /api/admin/system/resources/secret/entry
ref_config: .env
prereq: A deployed project: [Docker](/user-guide/deploy/local-docker), [Dokploy](/user-guide/deploy/dokploy) or a [Naas Space](/user-guide/deploy/production#naas-spaces).
next:
  - /capabilities/build/secrets/rotate-a-secret
  - /capabilities/deploy/security-compliance
---

# Add secrets

ABI reads its secrets through the **secret service**. A config file refers to one as `{{ secret.NAME }}`, and the service looks it up in the adapters listed under `services.secret.secret_adapters`, in order. A generated project lists one, the `dotenv` adapter on the project's `.env`. The other adapter types are `naas` (the Naas secret store), `base64`, `nats_rpc` and `custom`. Where `NAME` goes in production depends on how you deploy:

| Deployment | Where a new secret goes |
|---|---|
| Docker Compose (`abi deploy local`) | `.env`, next to `docker-compose.yml`. |
| Dokploy (`abi deploy dokploy`) | The Environment tab of the Dokploy application, filled from `.env.dokploy` (generated and gitignored). |
| Naas Space, from the CLI (`abi deploy naas`) | The `deploy.env` block of the config. |
| Naas Space, from the ABI repository's GitHub Actions | A repository secret, passed to the Space by `deploy_api.yml`. |

Never commit a secret. `.env` and `.env.*` are gitignored in a generated project.

## Reference it in the config

Whichever way you add the value, the config reads it the same way:

```yaml title="config.local.yaml"
modules:
  - module: naas_abi_marketplace.ai.openrouter
    enabled: true
    config:
      openrouter_api_key: "{{ secret.OPENROUTER_API_KEY }}"
```

The config is rendered once, when the engine starts: restart it after you add a value. A `{{ secret.X }}` that nothing defines stops the engine: with a terminal it prompts for the value, without one (a container, CI) it fails. `{{ secret.X }}` takes a process environment variable of that name before any adapter. Code that calls `secret.get` reads the `.env` file first, then the environment, unless the adapter sets `environment_first: true`. A full-line YAML comment is not rendered, so an example reference in a comment is safe.

## Web UI

Super admins only. Nexus writes the value through the running engine, to every secret adapter that accepts writes.

### Open the secrets view

Sign in to the deployed Nexus (`https://nexus.<domain>` on Dokploy), open **System** in the sidebar, then the **Data** tab, then **Secrets**.

### Create the secret

Choose **New secret**, enter the name and the value, then **Create**. Replacing an existing name asks you to type the name back. Every change is audited.

Where the value lands:

- **Docker Compose:** the project's `.env`, which the `abi` service mounts.
- **Dokploy:** the engine's secrets file on the `abi_secrets` volume, which survives redeploys.

### Restart the engine

Restart the `abi` service before a new `{{ secret.NAME }}` resolves. Module code that calls `secret.get` sees the value at once.

:::warning What Nexus cannot change
A key set in Dokploy's Environment tab wins over the secrets file again after a restart. Values that Compose interpolates into other services (`POSTGRES_PASSWORD`, the `NATS_*` passwords) and a Naas Space's `deploy.env` are not secret adapters: change them in the Config tab's places.
:::

## CLI

For a project whose secrets live in the Naas secret store (a `naas` adapter, or `base64` over it). The stored secrets are one env file, and a push replaces all of it.

### Read what is stored

```bash
abi secrets naas get-base64-env --naas-secret-name abi_secrets > .env.prod
```

### Add the line and push the whole file

```bash title=".env.prod"
PARTNER_API_TOKEN=the-value
```

```bash
abi secrets naas push-env-as-base64 --naas-secret-name abi_secrets --env-file .env.prod
```

A file that holds only the new line deletes every other secret. Both commands print every value: run them in your own terminal, never where the output is logged. `--naas-api-key` defaults to `$NAAS_API_KEY`. Delete `.env.prod` afterwards.

### Restart the engine

Redeploy, or restart the engine, so the config is rendered again. See [`abi secrets naas`](/user-guide/reference/cli#abi-secrets-naas).

## Python

### Write it from a module

```python title="src/my_module/__init__.py"
secret = self.engine.services.secret
secret.set("PARTNER_API_TOKEN", token)
```

`set` writes every adapter that accepts writes, in the running deployment. The module must declare the `Secret` service in its dependencies: see [Add a secret](/user-guide/build/secrets/add-a-secret?via=python).

### Restart for config

A module sees the value at once with `secret.get`. A `{{ secret.NAME }}` in the config resolves after the engine restarts.

## REST API

The System routes of the deployed Nexus API. Auth: the access token of a super admin, from `POST /api/auth/login`.

### Write the secret

```bash
curl -X PUT "https://api.example.com/api/admin/system/resources/secret/entry?id=PARTNER_API_TOKEN" \
  -H "Authorization: Bearer $NEXUS_TOKEN" \
  --data-binary 'the-value'
```

The body is the value, as UTF-8 text. Replacing an existing name needs `&confirm=PARTNER_API_TOKEN`; without it the call answers 409. The same limits as the Web UI apply.

### Restart the engine

Restart the `abi` service before a new `{{ secret.NAME }}` resolves.

## Config

### Docker Compose

Add `NAME=value` to `.env`, then recreate the services that use it:

```bash title=".env"
PARTNER_API_TOKEN=the-value
```

```bash
docker compose up -d --force-recreate
```

`abi deploy local --regenerate` re-renders the deployment files and keeps `.env`. The broker's passwords and signing secret (`NATS_ABI_PASSWORD`, `NATS_MODULE_PASSWORD`, `NATS_JWT_SECRET`) are generated into `.env` for you; do not copy them from another environment.

### Dokploy

Add the variable to the application's **Environment** tab, then **Deploy**. The tab is the source of truth: a value there wins over the engine's secrets file. `.env.dokploy` holds the starting values you paste into the tab. `abi deploy dokploy` keeps the ones already in it on later runs; `--regenerate` generates new secrets and backs up the previous file, so use it only to rotate them. See [Deploy with Dokploy](/user-guide/deploy/dokploy#secrets).

### Naas Space, with `abi deploy naas`

`abi deploy naas` builds the project's image, pushes it to a Naas registry and creates or updates the Space named in the `deploy` block of the config. The Space gets the variables listed under `deploy.env`, and only those:

```yaml title="config.prod.yaml"
deploy:
  workspace_id: "<your workspace id>"
  space_name: "my-space"
  naas_api_key: "{{ secret.NAAS_API_KEY }}"
  env:
    NAAS_API_KEY: "{{ secret.NAAS_API_KEY }}"
    OPENROUTER_API_KEY: "{{ secret.OPENROUTER_API_KEY }}"
    ABI_API_KEY: "{{ secret.ABI_API_KEY }}"
```

Put the value in `.env`, add a line to `deploy.env`, and run `abi deploy naas` again.

### The ABI repository's GitHub Actions

The ABI repository can deploy its own API to a Naas Space with `.github/workflows/deploy_api.yml`, after a release (see [Releasing](/developer-guide/releasing/release-process#containers-and-the-api-deploy)). That workflow is disabled in the repository's Actions settings: enable it first. To add a secret:

1. In the repository, open **Settings > Secrets and variables > Actions** and add the secret.
2. In `deploy_api.yml`, job `build`, step **Push latest abi container**, add it to the step's `env:` section, as the existing ones are.
3. In the same step, add it to `ENV_CONFIG`, the JSON the Space is created or updated with.
4. If the engine must also see it when the API is tested before the deployment, add it to the `docker run` of the step **Test API with production secrets before deployment**.
5. Commit and push.

Other jobs (`streamlit-demo`, `mcp-server`) build their own `ENV_CONFIG`.
