# Set environment variables

> Give an app settings and secrets as environment variables for its build, its replicas or both, and apply them with a deployment.

Source: https://docs.coritan.com/apps/environment-variables/

In the dashboard:

- /dashboard/apps/…/environment: https://www.coritan.com/dashboard/apps

Environment variables give an app its settings and secrets, such as a database address or a key for another service. Each variable reaches the build, the running app or both. You set them on the app's **Environment** tab or through the API. Once you save a value, neither the dashboard nor the API shows it again.

## Before you begin

- For an organization's app, you need the owner or admin role in the organization to change variables. Every member can see their names.
- A change reaches the app with its next deployment ([Apply a change](#apply-a-change)).

## Add a variable

1. Open the app from **Apps** in the [dashboard](https://www.coritan.com/dashboard/apps) and select the **Environment** tab.
2. Select **Add variable…**.
3. In **Name**, type the name, such as `DATABASE_URL`. In **Value**, type or paste the value.
4. Leave **Secret** ticked to hide the value in build logs, or untick it for a plain value ([Secret and plain values](#secret-and-plain-values)).
5. In **Available at**, choose **Build and runtime**, **Runtime only** or **Build only** ([Where a variable reaches](#where-a-variable-reaches)). For an image app, it starts at **Runtime only**.
6. Select **Add variable**.

A message confirms `DATABASE_URL added.`, and a note under the app's header says which kind of deployment applies it.

## Change or delete a variable

To give a variable a new value, open the menu at the end of its row and select **Replace value…**. Type the new value, change **Secret** or **Available at** if you need to, and select **Save variable**. Leave **Value** empty to keep the saved value while you change the other two. A message confirms `DATABASE_URL saved.`

A name cannot change. To rename a variable, delete it and add it again.

To delete a variable, select **Delete variable…** in its menu, then **Delete variable** to confirm. A message confirms `DATABASE_URL deleted.` Running replicas keep the variable until the next deployment, and the value cannot be recovered.

## Read the variable list

**Name**
: The variable's name, and `Secret, hidden in build logs` or `Plain`.

**Value**
: `•••• shop` for a value of eight characters or more, where `shop` is its last four characters, or `Sealed` for a shorter one.

**Available at**
: **Build and runtime**, **Runtime only** or **Build only**.

**Updated**
: When the variable last changed.

The card says how many variables the app has out of its limit, such as `12 of 200 variables. Values are sealed and never shown again.` With more than six, **Search variables** finds them by name.

## Names and values

A name has upper-case letters, digits and underscores, does not start with a digit, and has at most 128 characters, such as `DATABASE_URL`. These names are set by the platform, so you cannot use them:

- `PORT`, `TZ`, `STARTUP`, `SERVER_MEMORY`, `SERVER_IP` and `SERVER_PORT`
- any name that starts with `CORITAN_` or `P_SERVER_`

A value can be empty, and holds up to 32 KiB. The dashboard does not save an empty value, so set one through the API. An app has up to 200 variables, and their names and values together hold up to 256 KiB.

## Where a variable reaches

Each variable has a target:

Build and runtime
: The build and every replica get it. New variables have this target unless you choose another.

Runtime only
: Only the replicas get it.

Build only
: Only the build gets it. [Builds](/apps/how-apps-work/#builds) explains how a Railpack build and a Dockerfile build read it.

An image app is not built, so only the variables its replicas get reach it. Every replica also gets `PORT` and the `CORITAN_` variables ([What your app must do](/apps/how-apps-work/#what-your-app-must-do)).

## Secret and plain values

A variable is *secret* unless you say otherwise. A secret value of six characters or more appears as `[redacted]` in the build log, and a Dockerfile build gets it as a build secret instead of a build argument. A plain value can appear in the build log as it is.

Both kinds are stored encrypted, and neither is ever shown again after you save it. For a value of eight characters or more, the variable shows its last four characters, so you can tell which value is saved. To change a value, replace it.

## Apply a change

Replicas keep the variables they started with. After you change a variable:

- For one that the running app reads, redeploy the app.
- For one that the build reads, redeploy with a rebuild, so the build runs again.

[Redeploy](/apps/deploy-an-app/#redeploy) shows both. In the dashboard, a note under the app's header says which one: `A redeploy applies the saved changes`, or `A new build applies the saved changes` for a variable the build reads. Its **Redeploy…** button starts it. Through the API, the answer to each change says `redeploy_required` when a redeploy is needed.

## A value that cannot be read

Rarely, a saved value can no longer be opened on the platform, and the variable is marked as not readable. The running app starts without it, and a build that needs it fails with `The build variable API_KEY cannot be read on this platform. Save it again.` Set the variable again with its value.

In the dashboard, the **Environment** tab says `One variable cannot be read`, and the variable's value reads `Unreadable, set it again`. Select **Set again…** in its menu, type the value, and select **Save variable**.

## Result

The **Environment** tab lists each name with where it is available, whether it is secret, and the hint of its value. The next deployment uses them.

## Troubleshooting

`Use upper-case letters, digits and underscores, not starting with a digit, such as DATABASE_URL.` or `'database_url' is not a variable name: upper-case letters, digits and underscores, not starting with a digit, at most 128 characters`
: Change the name, for example to `DATABASE_URL`.

`PORT is set by the platform. Choose another name.` or `PORT is set by the platform and cannot be changed`
: The platform sets that name on every replica. Use another name, and read `PORT` to know where to listen.

`API_KEY needs a value`
: A new variable was saved without a value. Give it one.

`The environment is larger than 256 KiB in total`
: Shorten or delete some values. A large file is better kept in [Object Storage](/object-storage/) and fetched when the app starts.

A variable does not change in the running app
: Replicas keep the variables they started with. Redeploy the app, with a rebuild for a variable the build reads.

## Related

- [Deploy an app](/apps/deploy-an-app/)
- [How Apps works](/apps/how-apps-work/#builds)
- [Apps limits](/apps/limits/)

## With the API

For an organization's app, send the same requests under `/api/v1/orgs/{org_slug}/apps` ([organization apps reference](/api/reference/organizations/organization-apps/#op-get-api-v1-orgs-org-slug-apps-app-uuid-env)). In each request, `target` is `both`, `runtime` or `build`.

### List variables

[`GET /api/v1/client/apps/{app_uuid}/env`](/api/reference/client/apps/#op-get-api-v1-client-apps-app-uuid-env) lists the variables by name, without their values:

```bash
curl https://api.coritan.com/api/v1/client/apps/3c9e2f4a-8b1d-4f6e-a2c7-5d8b9e0f1a21/env \
  -H "Authorization: Bearer $CORITAN_TOKEN"
```

```json
{
  "vars": [
    {
      "key": "DATABASE_URL",
      "secret": true,
      "target": "runtime",
      "value": null,
      "hint": "shop",
      "readable": true,
      "created_at": "2026-09-20T09:12:00Z",
      "updated_at": "2026-09-24T16:40:00Z"
    },
    {
      "key": "NODE_ENV",
      "secret": false,
      "target": "both",
      "value": null,
      "hint": null,
      "readable": true,
      "created_at": "2026-09-20T09:12:00Z",
      "updated_at": "2026-09-20T09:12:00Z"
    }
  ]
}
```

`value` is always `null`. `hint` is the last four characters of a value of eight characters or more, and `null` otherwise. `readable` is `false` when the saved value can no longer be opened.

### Set one variable

[`PUT /api/v1/client/apps/{app_uuid}/env/{key}`](/api/reference/client/apps/#op-put-api-v1-client-apps-app-uuid-env-key) creates the variable or changes it. The body takes `value`, `secret` and `target`. A new variable needs `value`. For one that exists, leave `value` out to keep the saved value while you change `secret` or `target`.

```bash
curl -X PUT https://api.coritan.com/api/v1/client/apps/3c9e2f4a-8b1d-4f6e-a2c7-5d8b9e0f1a21/env/STRIPE_KEY \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"value": "sk_live_EXAMPLE0000", "target": "runtime"}'
```

```json
{
  "var": {"key": "STRIPE_KEY", "secret": true, "target": "runtime", "value": null, "hint": "0000", "readable": true},
  "created": true,
  "redeploy_required": true
}
```

`created` is `true` for a new variable. The example shortens `var`.

### Set many variables

[`PUT /api/v1/client/apps/{app_uuid}/env`](/api/reference/client/apps/#op-put-api-v1-client-apps-app-uuid-env) sets up to 200 variables in one request. Each item in `vars` has a `key` and the same `value`, `secret` and `target` as above. With `"replace": true`, every variable not in the list is deleted.

```bash
curl -X PUT https://api.coritan.com/api/v1/client/apps/3c9e2f4a-8b1d-4f6e-a2c7-5d8b9e0f1a21/env \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"vars": [{"key": "NODE_ENV", "value": "production", "secret": false}, {"key": "DATABASE_URL"}], "replace": true}'
```

```json
{
  "created": [],
  "updated": ["NODE_ENV"],
  "deleted": ["STRIPE_KEY"],
  "redeploy_required": true
}
```

`created`, `updated` and `deleted` name the variables the request touched. `DATABASE_URL` kept its value because the request left `value` out. The answer also holds `vars`, every variable after the change, as in the list above.

### Delete a variable

[`DELETE /api/v1/client/apps/{app_uuid}/env/{key}`](/api/reference/client/apps/#op-delete-api-v1-client-apps-app-uuid-env-key) deletes one variable and answers `{"deleted": "STRIPE_KEY", "redeploy_required": true}`. Running replicas keep it until the next deployment.

### Errors

| Status | `detail` | Cause |
| --- | --- | --- |
| `404` | `Variable not found` | The app has no variable with that name. |
| `422` | `{"message": "PORT is set by the platform and cannot be changed", "field": "key"}` | The name is one the platform sets. |
| `422` | `{"message": "API_KEY needs a value", "field": "value"}` | A new variable came without `value`. |
| `422` | `{"message": "The value of API_KEY is larger than 32 KiB", "field": "value"}` | The value is too long. |
| `422` | `{"message": "API_KEY appears twice", "field": "key"}` | `vars` names one variable twice. |
| `422` | `An app can have at most 200 variables` | The change would leave the app with more than 200. |
| `422` | `The environment is larger than 256 KiB in total` | The names and values would hold more than 256 KiB. |

## API

- `GET /api/v1/client/apps/{app_uuid}/env`: List an app's environment variables without their values (https://docs.coritan.com/api/reference/client/apps/#op-get-api-v1-client-apps-app-uuid-env)
- `PUT /api/v1/client/apps/{app_uuid}/env`: Set many variables; a variable given without value keeps its saved one (https://docs.coritan.com/api/reference/client/apps/#op-put-api-v1-client-apps-app-uuid-env)
- `PUT /api/v1/client/apps/{app_uuid}/env/{key}`: Set one environment variable, creating it when it is new (https://docs.coritan.com/api/reference/client/apps/#op-put-api-v1-client-apps-app-uuid-env-key)
- `DELETE /api/v1/client/apps/{app_uuid}/env/{key}`: Delete one environment variable (https://docs.coritan.com/api/reference/client/apps/#op-delete-api-v1-client-apps-app-uuid-env-key)
- `GET /api/v1/orgs/{org_slug}/apps/{app_uuid}/env`: List an app's environment variables without their values (https://docs.coritan.com/api/reference/organizations/organization-apps/#op-get-api-v1-orgs-org-slug-apps-app-uuid-env)
- `PUT /api/v1/orgs/{org_slug}/apps/{app_uuid}/env`: Set many variables; a variable given without value keeps its saved one (https://docs.coritan.com/api/reference/organizations/organization-apps/#op-put-api-v1-orgs-org-slug-apps-app-uuid-env)
- `PUT /api/v1/orgs/{org_slug}/apps/{app_uuid}/env/{key}`: Set one environment variable, creating it when it is new (https://docs.coritan.com/api/reference/organizations/organization-apps/#op-put-api-v1-orgs-org-slug-apps-app-uuid-env-key)
- `DELETE /api/v1/orgs/{org_slug}/apps/{app_uuid}/env/{key}`: Delete one environment variable (https://docs.coritan.com/api/reference/organizations/organization-apps/#op-delete-api-v1-orgs-org-slug-apps-app-uuid-env-key)
