Configure Atmos Stacks
Configure your Atmos stacks to work with Atmos Pro. Start with a basic stack definition and add workflow dispatches and component dependencies.
Atmos Pro reads your Atmos stack configuration to determine which components to deploy, in what order, and which workflows to dispatch. This page walks you through configuring your stacks for Atmos Pro.
If you're new to Atmos, start by creating a basic stack configuration. A stack defines what to deploy and where.
Each stack file lives in
stacks/ and has a name at the top that uniquely identifies the stack, followed by the components (Terraform root modules) to deploy:name: dev
components:
terraform:
vpc:
vars:
cidr_block: "10.0.0.0/16"
region: us-east-1
eks:
vars:
cluster_version: "1.29"
instance_types:
- t3.largeThe top-level
name identifies this stack. Component names (vpc, eks) are simple—they reference Terraform root modules in your components/terraform/ directory. Atmos Pro tracks each component/stack pair (e.g., vpc in dev) across plan, apply, and drift detection.For a step-by-step guide on adopting Atmos, see the migration guide. For complete documentation, see the Atmos Stacks documentation.
Add
settings.pro to your stack configuration to enable Atmos Pro. This tells Atmos Pro which workflows to dispatch for each event (pull request, merge, release) and enables features like drift detection.We recommend creating a mixin so you can apply the same Atmos Pro settings to all your stacks:
plan-wf-config: &plan-wf-config
atmos-terraform-plan.yaml:
inputs:
component: "{{ .atmos_component }}"
stack: "{{ .atmos_stack }}"
drift-detection-wf-config: &drift-detection-wf-config
atmos-terraform-plan.yaml:
inputs:
component: "{{ .atmos_component }}"
stack: "{{ .atmos_stack }}"
apply-wf-config: &apply-wf-config
atmos-terraform-apply.yaml:
inputs:
component: "{{ .atmos_component }}"
stack: "{{ .atmos_stack }}"
github_environment: "{{ .vars.stage }}"
settings:
pro:
enabled: true
drift_detection:
enabled: true
detect:
workflows: *drift-detection-wf-config
remediate:
workflows: *apply-wf-config
pull_request:
opened:
workflows: *plan-wf-config
synchronize:
workflows: *plan-wf-config
reopened:
workflows: *plan-wf-config
merged:
workflows: *plan-wf-config
merge_group:
checks_requested:
workflows: *apply-wf-config
release:
released:
workflows: *apply-wf-configThis mixin defines three workflow configurations using YAML anchors to avoid repetition. The
plan-wf-config dispatches the Atmos Pro plan workflow, the drift-detection-wf-config dispatches the same dedicated plan workflow for drift detection, and the apply-wf-config dispatches the Atmos Pro apply workflow with a github_environment input for deployment protection rules.The example above uses
{{ .vars.stage }} as the GitHub Environment name (e.g., dev, staging, production). If
you operate multiple tenants under one workspace, you can compose a finer-grained environment name like
{{ .vars.tenant }}-{{ .vars.stage }} — just make sure each rendered name matches a real GitHub
Environment in the repository.The
settings.pro block is the contract between your Atmos stack configuration and your GitHub Actions workflows. Atmos Pro reads this configuration after atmos describe affected --upload reports changed stacks, then dispatches the workflow files named under each event.Use these settings as the baseline for a repository that plans pull requests, participates in merge queue, handles merged PR uploads, and supports drift detection:
| Setting | Dispatches | Typical workflow |
|---|---|---|
settings.pro.pull_request.opened.workflows | PR opened | atmos-terraform-plan.yaml |
settings.pro.pull_request.synchronize.workflows | PR updated | atmos-terraform-plan.yaml |
settings.pro.pull_request.reopened.workflows | PR reopened | atmos-terraform-plan.yaml |
settings.pro.pull_request.merged.workflows | PR merged | plan, apply, or verification |
settings.pro.merge_group.checks_requested.workflows | PR entered GitHub merge queue | atmos-terraform-apply.yaml |
settings.pro.drift_detection.detect.workflows | Scheduled drift checks | atmos-terraform-plan.yaml |
settings.pro.drift_detection.remediate.workflows | Drift remediation | atmos-terraform-apply.yaml |
settings.pro.release.released.workflows | GitHub release published | atmos-terraform-apply.yaml |
The GitHub Actions workflow that runs
atmos describe affected --upload and this settings.pro block must agree. If the workflow uploads for pull_request.merged, configure settings.pro.pull_request.merged.workflows; if it uploads for merge_group.checks_requested, configure settings.pro.merge_group.checks_requested.workflows. If a lifecycle should not dispatch anything, skip the upload for that lifecycle in GitHub Actions.Each workflow entry is keyed by the workflow filename in
.github/workflows/. Its inputs map is rendered with Atmos Go templates for each affected component and stack:atmos-terraform-plan.yaml:
inputs:
component: "{{ .atmos_component }}"
stack: "{{ .atmos_stack }}"The plan workflow must accept
component and stack inputs. The apply workflow should accept component, stack, and github_environment. The workflows themselves are dedicated Atmos Pro workflows and should run their Atmos commands with the --upload flag.For complete workflow YAML examples, see GitHub Actions Workflows.
The workflow filename keys (e.g.,
atmos-terraform-plan.yaml, atmos-terraform-apply.yaml) are significant — Atmos
Pro uses them to determine the command type (plan, apply, detect, remediate) for each deployment. Use descriptive
filenames that include the command keyword. See Workflow Naming
Conventions for details.Atmos Pro participates in GitHub's merge queue without any GitHub App reconfiguration. When a PR enters the queue, GitHub creates a synthetic merge commit on a
gh-readonly-queue/<base>/pr-<N>-<sha> branch and fires a check_suite event that Atmos Pro recognizes. A new "Atmos Pro" check run is reported on the synthetic SHA so it can satisfy required-status-check enforcement on the queue.The lifecycle model is
pr -> queue? -> merge -> release?. Queue and release are optional, but merge still happens when merge queue is enabled. Each lifecycle may dispatch plan, apply, verification, or nothing. Atmos Pro does not assume which command you want; it validates that every lifecycle upload has matching settings.pro workflows for the affected component/stack instances.pull_request:
opened: { workflows: *plan-wf-config }
synchronize: { workflows: *plan-wf-config }
reopened: { workflows: *plan-wf-config }
merged: { workflows: *plan-wf-config }
merge_group:
checks_requested: { workflows: *apply-wf-config }Apply runs against the synthetic merge SHA. Apply succeeds → GitHub merges; apply fails → merge is blocked. The merged lifecycle remains configured because the affected-stacks workflow uploads after the PR actually merges; this example uses plan/verification there to avoid applying twice.
A few more things to know:
- Zero-config fallback. If you omit
settings.pro.merge_groupbut the queue is enabled, Atmos Pro falls back tosettings.pro.pull_request.synchronize.workflows(plan). Safe, but explicit lifecycle config is easier to reason about once queue or merge behavior differs from PR behavior. - GHA
on:block. The workflow that runsatmos describe affected --uploadmust also listmerge_groupunder its ownon:triggers — Atmos Pro can only dispatch downstream workflows for SHAs that GitHub actually fires events on. See GitHub Actions Workflows for the workflow YAML. - Merged lifecycle. If the affected-stacks workflow includes
pull_request.closedand uploads only whengithub.event.pull_request.merged == true, configurepull_request.mergedfor affected instances. If you do not want post-merge dispatch, skip that upload path in GitHub Actions.
Scope
describe affected --upload on merge_group. When merge_group.checks_requested dispatches apply, every affected stack the upload reports gets applied on the queue commit. A PR that touches a defaults mixin imported by every stack will fan apply across dev, staging, and prod on a single queue commit. Constrain the queue-side upload to the stack you intend to apply:# In the affected-stacks workflow, on the merge_group branch:
atmos describe affected --stack dev --uploadIf multi-stack queue apply is intentional, document the decision in your repo so the next reader doesn't reach for the same scope.
PR-time visibility requires
pull_request in the workflow on: block. Adding merge_group to the affected-stacks workflow gates the queue, but the workflow must also list pull_request for PR comments to populate. If it doesn't, you'll see the "no affected stacks workflow detected" admonition on every PR — that's expected behavior, not a product bug.Bootstrap order when first enabling merge queue. If you enable
Atmos Pro as a required status check on the merge queue in the same PR that adds merge_group.checks_requested to your Atmos Pro mixin, the PR can't merge: its own queue commit reads main's old config, which doesn't dispatch on merge_group, so the required check never fires. Land the merge_group.checks_requested config on main first (admin merge, or with Atmos Pro not yet required), then add Atmos Pro to the merge queue's required status checks list.Then, in your stack configuration, import the mixin to apply the settings to all stacks:
name: dev
import:
- mixins/atmos-pro/default
components:
terraform:
vpc:
vars:
cidr_block: "10.0.0.0/16"
region: us-east-1To ensure your components are deployed in the correct order, define dependencies between components using the
dependencies.components list. This is crucial for complex environments where certain components must be available before others can be deployed.All dependencies in Atmos are explicit — Atmos does not infer deployment order from remote state references or any
other implicit relationships. You must declare every dependency in the
dependencies.components list for Atmos Pro to
orchestrate the correct deployment order.For example, you might need to ensure that a database is created before an API is provisioned, or that a frontend is ready before a CDN is configured to serve it.
components:
terraform:
api:
dependencies:
components:
- component: database
- component: clusterHere is a fuller example showing multiple components with their dependency chains:
components:
terraform:
api:
dependencies:
components:
- component: database
- component: cluster
frontend:
dependencies:
components:
- component: api
- component: cache
cdn:
dependencies:
components:
- component: object-storage
- component: frontendEach dependency object can specify:
component(required)- The name of the component this component depends on
namespace(optional)- If the component is in a different organization
tenant(optional)- If the component is in a different organizational unit
environment(optional)- If the component is in a different region
stage(optional)- If the component is in a different account
If no context variables are provided, the dependency is assumed to be within the same stack. You can also specify dependencies across different stacks by providing the appropriate context variables.
On pull requests, Atmos Pro dispatches Terraform plan workflows in dependency order, but those plans do not apply upstream
changes. A downstream component still reads the currently-applied remote state for its dependencies. If an upstream
component will create, remove, or change outputs, the full impact on dependent components may not be visible until after
that upstream component has been applied and the dependents are planned again.
For example, if a pull request upgrades a Kubernetes cluster from
1.29 to 1.30, dependent components can often still
validate their Terraform runs during the pull request. However, some changes may only appear after the cluster upgrade is
applied and those components read the updated cluster outputs, provider behavior, add-on versions, or generated values.In these cases, the pull request plans validate that the affected Terraform runs can execute against the currently-known
configuration and state. They are not a guarantee that every downstream diff is knowable before upstream dependencies
exist or have been updated. After merge, Atmos Pro applies upstream dependencies first, then plans and applies dependent
components against the updated remote state.
This is the same boundary Terraform exposes inside a single plan when provider-computed values are not available yet:
some values are only known after apply. Ordered deployments make that dependency explicit across components and stacks.
New environments often have dependencies whose remote state does not exist yet. A first deployment might need an
application component to plan before the cluster, database, or network component has produced real outputs. In that
bootstrap phase, use
!terraform.state with YQ defaults so Atmos
can pass mock values to Terraform when the upstream state or output is not available yet.components:
terraform:
api:
vars:
cluster_endpoint: !terraform.state cluster ".endpoint // ""https://cluster-not-created-yet.example.invalid"""
cluster_ca_data: !terraform.state cluster ".cluster_ca_data // """""
cluster_security_group_id: !terraform.state cluster ".security_group_id // ""sg-00000000000000000"""
private_subnet_ids: !terraform.state vpc ".private_subnet_ids // [""subnet-00000000000000000"", ""subnet-11111111111111111""]"The value before
// is the real Terraform output to read from remote state. The value after // is the default Atmos
uses when the dependency has not been provisioned yet or when the output is missing. After Atmos resolves the stack,
Terraform receives ordinary input variables:variable "cluster_endpoint" {
type = string
}
variable "cluster_ca_data" {
type = string
}
variable "cluster_security_group_id" {
type = string
}
variable "private_subnet_ids" {
type = list(string)
}Keep mock values clearly named, structurally valid, and safe to expose in plans. They should be good enough to validate
types, provider configuration, and module wiring, but they should not hide real production dependencies or contain
secrets. Atmos only uses these defaults for recoverable cases, such as missing state or missing outputs. Backend access
failures, permission errors, and other infrastructure failures should still fail the plan instead of being masked by a
mock value.
For more detailed information about configuring dependencies, refer to the Atmos documentation on dependencies. You can also use the
atmos describe dependents command to explore dependency relationships in your configuration.Next: Configure Atmos CI
Set up Atmos native CI integration to automatically update commit statuses, create job summaries, and export plan results.