Implement Feature Flags using Tags and Labels
Two workloads can run in production and need different configurations because only one is PCI-scoped. An environment name alone cannot express that context. Native Atmos tags and labels can now act as feature flags: use them to select mixins through conditional imports, then find the components with that behavior enabled.
The Problem
Inheritance already lets you give dev and prod different settings. The harder case is behavior that depends on context across those environments: enable a retention policy wherever a workload is PCI-scoped.
You can import the PCI mixin explicitly in each applicable stack. But if you also label those stacks to find and
operate on them, you maintain the same decision twice: the classification and the import. A shared configuration
should be able to read pci: true and select the behavior itself.
The Fix
Stack imports now accept a when condition that can read native stack-wide
metadata.labels and metadata.tags. In this example, the pci label is a feature flag: it selects the retention
mixin and identifies the resulting component instances in list commands and other commands that support native selectors.
The application catalog reads its log retention from settings, with a fallback of 7 days.
A shared import rule will select the PCI setting wherever the flag is enabled:
components:
terraform:
app:
vars:
log_retention_days: !template '{{ .settings | dig "default" "log_retention_days" 7 }}'
The !template function preserves the numeric value. The dig lookup returns 7 when
the setting is absent. Templates and Sprig are enabled by default with the current configuration edition.
The PCI mixin supplies a shared setting that any component can consume:
settings:
default:
log_retention_days: 366
For example, PCI DSS v4.0.1, requirement 10.5.1 calls for retaining audit logs for at least 12 months, with the most recent three months immediately available for analysis.
Here we use 366 days to account for leap years.
Put the conditional import in one shared environment configuration:
import:
- catalog/app
- path: mixins/pci
when: !cel labels.pci == true
Each stack imports that same configuration and supplies its classification. For this example, dev is outside PCI scope and prod is inside it:
name: dev
metadata:
labels:
pci: false
import:
- catalog/environment
name: prod
metadata:
labels:
pci: true
import:
- catalog/environment
The environment's labels are available inside the shared configuration's nested imports. The same rule skips the PCI
mixin for dev, retaining 7 days, and includes it for prod, selecting 366 days. The environment stacks contain no
conditional logic; that rule is defined once in catalog/environment.yaml. The condition reads labels.pci, so
any stack with pci: true gets the mixin, regardless of its environment name.
Atmos evaluates the import condition, merges the selected settings, then renders the component's variable. The label selects the behavior, the mixin supplies the setting, and the component decides how to use it. The same label also identifies the component in list commands.
Configure, Find, and Inspect
- Install Atmos
- Try the example
Copy the example into a new directory and make it your own:
atmos init examples/conditional-imports
cd conditional-imports
# Find the component instances classified by the label.
atmos list instances --labels pci:true
# Inspect the configuration selected by that metadata.
atmos describe component app -s prod --query .vars
# log_retention_days: 366
atmos describe component app -s dev --query .vars
# log_retention_days: 7
The list instances command shows both the component and stack, so different instances of the same component remain distinguishable.
Other commands that support these selectors include list components, list stacks, describe affected, and bulk
Terraform operations. Use the selected component and stack with describe component to inspect its configuration.
The example requires no cloud credentials and creates no cloud resources. Its Terraform component displays the selected
configuration with terraform_data.
Keep the Classification Consistent
Import conditions use literal stack-wide metadata available when each import is reached. Earlier imports supply defaults; the current manifest's labels override those defaults and flow into nested imports. Later imports do not change decisions already made.
For the simplest connection between configuration and selection, declare the classification in the top-level stack and let its components inherit it. Component-specific overrides or later imports can change the metadata selectors see after the import decision. Computed metadata is unavailable to import conditions, even when it can be resolved for later selection.
Use an unquoted boolean for labels.pci == true. The string "true" does not satisfy that expression, although CLI label
selectors stringify both values. For an optional label use
has(labels.pci) && labels.pci == true.
Checked-in metadata keeps local and CI configuration consistent. Conditions can also use CI detection (ci, local),
process environment variables (env), and combinations of conditions. An import whose condition does not match is not
read and contributes no configuration. See the import reference for the full rules.
Template Defaults and Editions
A configuration edition pins Atmos defaults to a date, so upgrading Atmos can preserve those defaults until you choose to advance the pin. Browse the edition history to see what changes between dates.
Projects without an edition pin, or pinned to 2026-10-26 or later, now enable stack templates, Sprig, and Gomplate
when their settings are omitted from atmos.yaml. Earlier editions preserve the disabled defaults for configurations
that omit these settings. Explicit enabled: true or enabled: false values always win. See
template configuration when keeping an older edition pin.
Get Involved
Share ways you use native metadata to configure and select components in the Atmos repository.
