atmos secret
The atmos secret command group provides a GitOps-friendly, multi-cloud workflow for managing secrets. Secrets are declared in your stack configuration (committed to git) and their values are stored in a cloud secret backend (AWS SSM, AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, GCP Secret Manager) or a SOPS-encrypted file. Declared secrets are resolved at runtime with the !secret YAML function and are automatically masked in all output.
Usage
atmos secret <subcommand> [flags]
Subcommands
init- Provision declared secrets, prompting for any that are missing.
set(aliasadd)- Set a declared secret's value (create or update).
get- Retrieve a declared secret's value.
delete(aliasrm)- Remove a declared secret's value from its backend.
list- List declared secrets and their initialization status.
pull- Download declared secrets to a local file for development.
push- Upload secret values from a local file (all keys must be declared).
import- Import secret values from a file, skipping undeclared keys.
validate- Validate that all required declared secrets are initialized.
exec- Run a command with declared secrets injected as environment variables.
shell- Launch an interactive shell with declared secrets in the environment.
How It Works
-
Configure a secret backend in
atmos.yaml. A store becomes a secret backend by settingsecret: true:stores:
app-secrets:
type: aws-secrets-manager
secret: true
identity: aws/prod-secrets
options:
region: us-east-1
prefix: atmos/secrets -
Declare secrets in your stack/component config under
secrets.vars:components:
terraform:
api:
secrets:
vars:
DATADOG_API_KEY:
description: "Datadog API key"
store: app-secrets
required: true
vars:
datadog_api_key: !secret DATADOG_API_KEY -
Provision values with the CLI:
atmos secret set DATADOG_API_KEY --stack=prod --component=api
atmos secret list --stack=prod --component=api
Secret scopes
Every secret has an explicit scope that controls where its value is stored. Two scopes are
available (a cross-stack global scope is planned):
stack— declared at the top level of a stack (asecrets:block that is a sibling ofvars:). Stored once per stack and shared by every component instance. Rotate it once and every instance sees the new value.instance(default) — declared under a component. Stored per component instance.
Scope is derived from where the secret is declared, not written by hand:
# stack manifest (top level): SHARED_TOKEN is stack-scoped — one value for the whole stack
secrets:
vars:
SHARED_TOKEN: { sops: default }
components:
terraform:
api:
secrets:
vars:
# Re-declaring SHARED_TOKEN under a component pulls it to INSTANCE scope for `api` only,
# which now owns its own value. Other components keep using the shared stack value.
SHARED_TOKEN: { sops: default }
# API_KEY is declared only here → always instance-scoped.
API_KEY: { sops: default }
The rule is one-way: a stack secret can be pulled down to instance scope by re-declaring it
under a component, but an instance-declared secret can never become stack-scoped. The override is
opt-in — an instance only carries its own value if it declares the secret; setting an instance
value for a secret that is still stack-scoped at that component is a hard error (declare it under
the component first, or omit --component to set the shared stack value).
Each backend maps scope to its native primitive and rejects scopes it can't represent. For SOPS,
the file is derived from scope under spec.path (default secrets/): stack →
secrets/<stack>.enc.yaml, instance → secrets/<stack>.<instance>.enc.yaml. A changed SOPS file is
treated as an automatic dependency by atmos describe affected, so rotating a secret marks the
components that consume it.
Interactive prompts
When --stack or --component is missing and you are in an interactive terminal, Atmos prompts you
to choose one (the same built-in selector used elsewhere). atmos secret set and atmos secret init
with no arguments walk you through stack → component → secret. In CI / non-interactive shells these
prompts are disabled and a missing required flag returns the standard error, so scripts are
unaffected.
Masking and Inspection
atmos secret get, pull, and push always retrieve real values to do their job; the values are masked in display output unless you pass --mask=false.
Inspection commands (describe and the list family) resolve !secret to <MASKED> without contacting the backend when masking is enabled (the default) — so you can inspect a stack's shape with no cloud credentials. Pass --mask=false to retrieve and reveal the real values.