---
title: Environment and Secrets Guardrails
description: See every environment variable a stack uses, where it comes from, and whether it is likely a secret, without ever exposing a value. Sencho derives a per-stack env inventory, flags missing and duplicate variables, and can block a deploy when a required variable has no value.
---

The **Environment** tab in the right-hand **Anatomy** panel answers a question Compose makes surprisingly hard: *which environment variables does this stack actually use, where does each one come from, and is anything missing or sensitive?* Sencho derives the answer from the stack's Compose files and env files and presents it as a single inventory.

The inventory is advisory and read only. It never changes a stack, and it works entirely from variable **names**: a value is never read into the report, the checklist, or the logs, so nothing sensitive is exposed.

## Interpolation versus container injection

Compose treats environment in two distinct ways, and mixing them up is a common source of "it works on one host but not another":

- **Interpolation.** A `${VAR}` reference in the Compose file is resolved from the project `.env` file and the shell environment. It substitutes a value into the file before the container is created.
- **Container injection.** Values under a service's `environment:` block and in any `env_file:` are handed to the running container. They are never used to resolve `${VAR}` in the Compose file.

The inventory labels each variable with how it is used, so you can tell at a glance whether a variable feeds Compose interpolation, is injected into a service, or both.

<Note>
  Interpolation resolves a `${VAR}` into the Compose file before the container is created, so a variable used in a **structural** field (a bind path, a network name, a published port, or an `extra_hosts` entry) is substituted before any tab reads the model. Its resolved value then shows in the Storage, Networking, and Dossier facts to anyone with read access to the stack, the same access that can open the stack's Compose and `.env` files. Container injection is different: values under `environment:` and `env_file:` are only ever reported by name. Keep secrets in injection, and avoid interpolating them into structural fields.
</Note>

## What the inventory shows

For every variable, Sencho records its source, its scope, and a status:

| Status | Meaning |
|--------|---------|
| **Present** | Referenced or injected, and defined in a stack-local source. |
| **Missing** | Referenced by the Compose file but not set anywhere, so Compose substitutes an empty string or fails on a required variable. |
| **Unused** | Defined in the project `.env` but never referenced or injected. |
| **Duplicate** | Defined in two different places, which can resolve to different effective values. |
| **Shell-only** | Referenced and resolved only from the shell of the host, not persisted with the stack, so it will not follow the stack to another node. |

Variables whose name suggests a secret, such as `DB_PASSWORD`, `API_KEY`, or `CLIENT_SECRET`, are marked with a lock and show presence only. Their value is never read, so it cannot appear in the inventory, the checklist, or anywhere else.

### Copy env checklist

The **copy env checklist** action copies the inventory as a Markdown checklist of variable names, status, and source. It is built for sharing in a ticket or a runbook, so it deliberately contains no values, including for likely secrets.

## Preflight: missing env files

When a service declares an `env_file:` that does not exist in the stack directory, Compose refuses to start the stack. Sencho surfaces this as a **high-risk** finding in the [Compose Doctor](/features/compose-doctor) preflight, naming the file and the service that declares it, so you catch it before you deploy. An entry marked `required: false`, and a path that Sencho cannot resolve, are not reported.

## Blocking a deploy on missing required variables

A `${VAR:?message}` reference tells Compose the variable is required: the deploy fails if it is unset or empty. By default Sencho surfaces that as an advisory finding and lets Compose report it at deploy time.

If you would rather fail fast with a clear message before anything runs, turn on **Block deploy on missing required env vars** under **Settings → Infrastructure → Stacks → Deploy Guardrails**. With it on, a deploy or update is refused up front when a required variable is unset or empty, before any backup, image pull, or container change happens. It is off by default, applies to the node you set it on, and requires an admin to change.

## Opening the inventory

1. Click any stack in the left sidebar to open it.
2. Switch to the **Environment** tab in the Anatomy panel header.
3. Review the variables grouped by status, and use **copy env checklist** to share a values-free summary.

The inventory is built against the **active node**, so selecting a remote node inspects the stack on the machine that owns it.

## Troubleshooting

<AccordionGroup>
  <Accordion title="A variable I set in .env is marked unused">
    The project `.env` is read for Compose `${VAR}` interpolation. A variable that lives only in `.env` and is never referenced by the Compose file, and never injected into a service, has nothing using it. Reference it with `${VAR}`, move it into a service's `environment:` or `env_file:` if the container needs it, or remove it.
  </Accordion>
  <Accordion title="A variable shows as shell-only">
    The variable resolves from the host shell that Sencho runs in, not from a file stored with the stack. It works on this host but will not travel with the stack to another node. Add it to the project `.env` or an `env_file:` so the stack carries its own configuration.
  </Accordion>
  <Accordion title="The same key counts once even though it is in .env and env_file">
    When the project `.env` is also listed as an `env_file:`, it is one physical file doing two jobs: interpolation and injection. Sencho counts it once, so this common setup is never flagged as a duplicate. A duplicate means the key is genuinely defined in two different locations.
  </Accordion>
  <Accordion title="A likely secret is flagged that is not actually sensitive">
    The classification is a heuristic based on the variable name, and it errs toward marking things as secret. Marking a non-secret as a likely secret only hides a value that the inventory never reads anyway, so there is no downside; the variable still shows its name, source, and status.
  </Accordion>
  <Accordion title="The Environment tab is not there on a remote node">
    The tab appears when the active node reports that it supports the env inventory. A node running an older version of Sencho does not advertise it, so the tab is hidden for that node until it is updated.
  </Accordion>
</AccordionGroup>
