# Set environment variables

> Plain values, secrets and links — per environment, per app, or per target — and how they reach the container

URL: https://pier.run/docs/guides/set-environment-variables

**Goal.** `api` reads `LOG_LEVEL` and a secret `API_KEY` from its environment, with the
secret never written to `pier.yaml`.

### A plain value

```bash
pier vars set LOG_LEVEL=info --app api
```

```text title="output"
→  Updated pier.yaml

  Changes:
    + create env_var "LOG_LEVEL" in prod (plain)

OK  Set LOG_LEVEL on api in prod

```

`--app api` scopes the variable to the app. Without it, the variable is set on the
environment and every app and site in it sees it; `--app api/canary` scopes it to one
target. A narrower scope wins over a wider one with the same key.

### A secret

```bash
pier vars set API_KEY --secret --secret-value 'sk_live_9f2e4c…' --app api
```

```text title="output"
→  Updated pier.yaml

  Changes:
    + create env_var "API_KEY" in prod (secret)

OK  Set API_KEY on api in prod (secret)

```

The declaration (`API_KEY: { secret: true }`) is written to `pier.yaml`; the value is
stored in the platform's vault only. `--secret-value -` reads the value from stdin;
`pier vars import --file .env --secret` loads several at once.

### Check what is set

```bash
pier vars
```

```text title="output"
                                         
 KEY       VALUE    SECRET ENV  UPDATED  
                                         
 API_KEY   ******** yes    prod just now 
 LOG_LEVEL info            prod just now 
                                         

```

```bash
pier vars reveal API_KEY
```

```text title="output"
API_KEY=sk_live_9f2e4c…

```

### Roll it into the container

A variable change is recorded as a new environment version; the running container keeps
its old environment until the next deploy. `pier deploy api` releases it without a
rebuild: the plan says `deployed inline` and counts the pending variables.

```bash
pier deploy api
```

```text title="output"
→  Comparing prod environment against platform state...

  Changes:
    ~ update app "api" in prod
      env_version: 0 → 2  (env var — next deploy)

  Deploy:
    app "api"    deploy (deployed inline)

  Env vars:
    app "api"    2 pending

OK  Applied 1 config change(s) to prod
→  Waiting for infrastructure provisioning...
OK  Infrastructure ready

OK  Summary: 1 deployed

```

```bash
pier ssh api -- env | grep -E '^(LOG_LEVEL|API_KEY)='
```

```text title="output"
API_KEY=sk_live_9f2e4c…
LOG_LEVEL=info

```

## What changed

```yaml title="pier.yaml"
    apps:
      - name: api
        build_context: ./api
        env:
          API_KEY:
            secret: true
          LOG_LEVEL:
            value: info
```

The file can be committed: it holds no secret value. Applying it from another checkout
produces the same declaration, and the container receives the same vault value.

## Variations

* **Link a resource** instead of pasting its URL: `pier vars set DATABASE_URL --from
  postgres.main.url --app api` — [Add Postgres to an app](/docs/guides/add-postgres-to-an-app).
* **Edit the file** and apply: the `env:` block accepts `value`, `secret: true`,
  `from`, and `required: false`; `pier apply` converges it.
* **Remove**: `pier vars rm LOG_LEVEL --app api`.
* **Per environment**: `-e staging` on any of these commands targets that environment
  only; environments never share a variable.

## See also

[Environment variables](/docs/vars) · [`pier vars`](/docs/cli/reference/vars) ·
[Concepts: environment](/docs/concepts#environment)
