Files
sencho/docs/tutorials/configure-environment-guardrails.mdx
T
Anso de7ecee497 docs: tutorials batch 1 (#1656)
* docs: scaffold Tutorials tab and write enroll-a-remote-node

Adds the Tutorials tab to docs.json with 15 stub pages across three
groups (Fleet & nodes, Deploy & automate, Secure & integrate), and
writes the first full tutorial: enrolling a remote node via Pilot
Agent mode, verified end to end against a live control instance and
a second host running an existing Jellyfin Compose stack.

* docs: write Schedule an Operation tutorial

* docs: fix MDX parse error in Schedule an Operation tutorial

* docs: write Set Up SSO with Custom OIDC tutorial

Registers an OAuth client in a self-hosted identity provider (Keycloak
worked example), configures Sencho's Custom OIDC settings, tests the
connection, and verifies a real end-to-end login with auto-provisioning
from two independent surfaces.

* docs: drop unused SSO tutorial screenshot

sso-settings-empty.png isn't referenced by the tutorial content.

* docs: write Set Up Fleet Federation tutorial

Migrates a Blueprint-managed workload from one node to another using
pin and cordon, with the confirm-before-mutate rollout in between.
Corrects the published feature page's claim that pin requires the
global admin role; the code gates cordon and pin identically, scoped
to the target node.

* docs: write Create and Approve a Blueprint tutorial

Covers labeling a target node, authoring a stateless Blueprint,
walking through the create-then-approve rollout flow, verifying
from the Deployments tab and the audit log, and recovering from a
port-conflict deploy failure. Cross-links with Move a Blueprint
Deployment to a New Node in both directions.

* docs: write Automatically Patch a Stack With an Auto-Update Label tutorial

* docs: write Configure Auto-Heal Policies tutorial

Adds the full step-by-step content for the Configure Auto-Heal Policies
stub: an nginx+redis scenario stack, adding a service-scoped policy,
and a live verification that breaks a container's healthcheck,
confirms the policy restarts it, and recovers it.

* docs: write Set Up Deploy Enforcement tutorial

Covers configuring a block-on-deploy scan policy against a stack running
a deliberately outdated nginx image, reading the block dialog, and
overriding it as an admin with the bypass confirmed in the audit log.
Includes a stack-pattern mismatch as the most likely first-time failure.

* docs: write Configure Environment Guardrails tutorial

Covers the Block deploy on missing required env vars guardrail end to
end: deploy a Postgres stack with a required password, enable the
guardrail, watch a real update get refused with a named-variable
message, fix it, and verify from the Activity and Environment tabs.

* docs: write Deploy a Stack Automatically From Your CI Pipeline tutorial

* docs: write Catch and Fix a Container That's Drifted From Its Compose File tutorial

Covers reading a real Drift finding after an out-of-band container
change and resolving it by redeploying through Sencho.

* docs: write Connect a Git Source tutorial

* docs: write Push a Shared Environment File to Every Node tutorial

Writes the Fleet Secrets tutorial: create a bundle, target nodes by
label, read the push preview/results, verify via the audit log, and
recover from a stack-name typo. Removes the three unwritten
placeholder stubs (RBAC, Sencho Mesh, private registries) that had no
scheduled content.
2026-08-08 01:49:21 -04:00

118 lines
9.7 KiB
Plaintext

---
title: Stop a Deploy When a Required Environment Variable Is Missing
sidebarTitle: Block a missing required var
description: Turn on the deploy guardrail that refuses a stack update when a required environment variable has no value, and watch it catch a real missing password before anything breaks.
---
Say a stack declares a required database password with `${DB_PASSWORD:?message}` in its Compose file. Nothing stops a teammate from clearing that value in `.env` by accident, and a normal `docker compose up` would let the mistake ride until the container fails at runtime. This walks through turning on the guardrail that catches it up front instead: you'll deploy a small Postgres stack with a required password, clear the password to simulate the mistake, watch the next update get refused with a clear message naming the variable, then fix it and confirm the update goes through.
This tutorial covers one guardrail: **Block deploy on missing required env vars**. It doesn't cover the rest of the Environment inventory (secret classification, duplicate or shell-only detection, project environment file selection) or the other Deploy Guardrails settings (health observation, rollback retention, automatic external network creation); see the [Environment and Secrets Guardrails](/features/environment-guardrails) feature page for the complete picture.
## Prerequisites
- The `admin` or `node-admin` role. Changing the guardrail setting needs `node:manage` permission, which both roles hold; updating the stack needs `stack:deploy`, which every role except `viewer` and `auditor` holds.
- Available on every tier; no Admiral requirement.
- The setting is scoped to one node. If you manage more than one, turn it on for the node you deploy the scenario stack to.
<Steps>
<Step title="Deploy a stack with a required password">
Select **Create Stack**, name it `inventory-db`, and replace its Compose file with:
```yaml
services:
db:
image: postgres:16
restart: always
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD in .env before deploying}
POSTGRES_DB: inventory
ports:
- "5432:5432"
```
The `${DB_PASSWORD:?message}` syntax tells Compose the variable is required: it refuses to render the stack at all if `DB_PASSWORD` has no value, anywhere in the file. The **.env** tab stays disabled until a project environment file exists, so switch to the **Files** tab, select **New file**, and create one named `.env`. Open it and add `DB_PASSWORD=devpassword123` before your first deploy. Setting it now gets you a clean first deploy; the next steps remove it on purpose to see the guardrail react.
<Frame>
<img src="/images/tutorials/configure-environment-guardrails/compose-required-password.png" alt="Compose editor for inventory-db showing a single db service: image postgres:16, restart always, environment with POSTGRES_PASSWORD set to the interpolation ${DB_PASSWORD:?Set DB_PASSWORD in .env before deploying}, POSTGRES_DB inventory, and port 5432 published." />
</Frame>
Select **Save & Deploy**. The container comes up healthy.
</Step>
<Step title="Confirm the variable in the Environment tab">
Open the stack and switch to the **Environment** tab in the Anatomy panel header. `DB_PASSWORD` appears under **present** with **secret** and **required** badges, sourced from `.env` and scoped to interpolation. The lock badge means its value is never read into the inventory, only its presence and status.
<Frame>
<img src="/images/tutorials/configure-environment-guardrails/env-tab-db-password-required.png" alt="Environment tab for inventory-db showing 3 vars, 2 likely secret, and a present group listing DB_PASSWORD (secret, required, present, source .env, scope interpolation), POSTGRES_DB (present, source inline, scope injected), and POSTGRES_PASSWORD (secret, present, source inline, scope injected)." />
</Frame>
</Step>
<Step title="Turn on the guardrail">
Open **Settings** → **Infrastructure** → **Stacks** and find the **Deploy Guardrails** section. Turn on **Block deploy on missing required env vars**, then select **Save settings**. It's off by default: without it, Compose only reports a missing required variable when the deploy or update actually runs.
<Frame>
<img src="/images/tutorials/configure-environment-guardrails/deploy-guardrails-setting-on.png" alt="Deploy Guardrails settings section with Block deploy on missing required env vars set to ON, alongside Observe health after updates, Observation window, Superseded rollback retention, Maximum retained rollback generations, and Automatically create missing external networks." />
</Frame>
</Step>
<Step title="Clear the password to simulate the mistake">
Back on `inventory-db`, open the **.env** tab and clear the value so the line reads `DB_PASSWORD=`. Select **Save & Deploy** and choose **Save Only** from the dropdown next to it, so the change is written to disk without triggering a deploy yet.
<Frame>
<img src="/images/tutorials/configure-environment-guardrails/env-file-password-cleared.png" alt=".env editor for inventory-db showing a single line, DB_PASSWORD with no value, with the stack still running above." />
</Frame>
</Step>
<Step title="Update the stack and watch the guardrail fire">
Select **Update**. A readiness dialog summarizes preflight, drift, current containers, and a few other checks; note that none of them mention environment variables; the dialog can say **ready** even though this update is about to be refused. Select **Update now** anyway.
The update fails immediately, before any image pull or container change, with **Deploy blocked: required environment variable DB_PASSWORD is missing. Define it in a .env or env_file, then deploy again.** The full message is easiest to read from the notification bell in the top bar.
<Frame>
<img src="/images/tutorials/configure-environment-guardrails/update-blocked-notification.png" alt="Notifications panel with the top entry reading 'Deploy blocked: required environment variable DB_PASSWORD is missing. Define it in a .env or env_file, then deploy again.', 2 minutes ago, above older entries for a vulnerability scan, a health gate pass, and an image update." />
</Frame>
</Step>
<Step title="Fix it and confirm the update succeeds">
Reopen the **.env** tab, set the line back to `DB_PASSWORD=devpassword123`, and save. Select **Update** again. This time it runs to completion: the image pulls, the container recreates, and the health gate passes.
<Frame>
<img src="/images/tutorials/configure-environment-guardrails/update-succeeded-health-gate.png" alt="Updating inventory-db progress dialog showing Succeeded, a passed health gate message, and a log ending in '=== Stack updated successfully ===' and '=== Pruned dangling images ==='." />
</Frame>
</Step>
</Steps>
## Verify it worked
Check from two independent surfaces so you're not trusting a single UI element.
**The Activity tab.** Open the stack's **Activity** tab. Reading newest first, you'll see the health gate pass and the successful update sitting right above the earlier **Deploy blocked** entry, so the block and the recovery are both on the record with the account that triggered each one.
<Frame>
<img src="/images/tutorials/configure-environment-guardrails/activity-tab-block-and-fix.png" alt="Activity tab for inventory-db listing, newest first: health gate passed, stack updated, update started, Deploy blocked: required environment variable DB_PASSWORD is missing, an earlier vulnerability scan and health gate pass, and the original deploy." />
</Frame>
**The Environment tab.** Reopen it: `DB_PASSWORD` is back under **present** with its **required** badge, and the summary line reads 0 missing.
## If something goes wrong
**The variable still shows missing after you set it, but in the wrong place.** Compose's `${VAR:?message}` interpolation only resolves from the project `.env` file (or the shell), never from a service's `env_file:` entries: those are only injected into the container after interpolation already ran. Moving `DB_PASSWORD` into a separate `env_file:`-declared file instead of the project `.env` reproduces the exact same block, because as far as interpolation is concerned the variable is still unset.
<Frame>
<img src="/images/tutorials/configure-environment-guardrails/compose-env-file-mistake.png" alt="Compose editor for inventory-db showing env_file: - secrets.env added under the db service, with POSTGRES_PASSWORD still set to the ${DB_PASSWORD:?...} interpolation." />
</Frame>
Move the value back into the project `.env` file (or add it there in addition to the `env_file`) and redeploy; the guardrail clears as soon as interpolation can see it. See [Environment and Secrets Guardrails](/features/environment-guardrails#interpolation-versus-container-injection) for the full interpolation-versus-injection explanation.
## Related
<CardGroup cols={2}>
<Card title="Environment and Secrets Guardrails" icon="triangle-exclamation" href="/features/environment-guardrails">
The full inventory: secret classification, env file status, and project environment file selection.
</Card>
<Card title="Compose Doctor" icon="stethoscope" href="/features/compose-doctor">
Pre-deploy preflight that also surfaces a missing `env_file:` as a high-risk finding.
</Card>
<Card title="Fleet Secrets" icon="lock" href="/features/fleet-secrets">
Author encrypted env-var bundles on the hub and push them to labeled stacks across the fleet.
</Card>
<Card title="Health-Gated Updates" icon="shield-check" href="/features/health-gated-updates">
The post-deploy health observation configured in the same Deploy Guardrails settings section.
</Card>
</CardGroup>