Atmos Pro Logo

Atmos Pro

ProductPricingDocsBlogChangelog
⌘K
Create Workspace
Atmos Pro Logo

Atmos Pro

ProductPricingDocsBlogChangelog
What is Atmos Pro?
Installation
How it Works
Workspaces
Team Members
Authentication
Ordered Deployments
Deployment Approvals
Deployment Locking
Drift Detection
Event Triggers
Workflow Dispatches
Repository Permissions
Audit Log
MCP Server
AI Credits
Troubleshooting
Workspaces
Atmos Stacks
Atmos CI
Atmos Toolchain
Cloud Authentication
GitHub Repository
GitHub Workflows
Commit From CI
Upload Instances
GitHub Environments
Deployment Locking
Drift Detection
CODEOWNERS Validation
Audit Log Webhooks
MCP Server
AWS
Private Cross-Repo Modules
Reference
Atmos Docs
Example Repository

GitHub Actions Workflows

Create the GitHub Actions workflows that Atmos Pro dispatches to plan, apply, and detect drift on your infrastructure.


Atmos Pro orchestrates your deployments by dispatching dedicated GitHub Actions workflows that you define for Atmos Pro. For security, Atmos Pro only dispatches workflows against your repository's default branch. This ensures your branch protection rules guard against manipulation — workflows must be merged before they take effect.
You'll create the following workflows in your repository:
  1. 1
    Affected Stacks — Runs on every pull request to detect which stacks changed
  2. 2
    Plan — Dispatched by Atmos Pro to run atmos terraform plan for each affected stack
  3. 3
    Apply — Dispatched by Atmos Pro when a plan is approved
  4. 4
    Upload Instances — Runs on merge to the default branch and on a schedule to power drift detection
The workflows below are single-purpose Atmos Pro workflows. Each one runs the relevant Atmos command with --upload so the command reports its result to Atmos Pro.
For a working Atmos Pro repository, configure these four workflow files plus the matching settings.pro stack configuration:
FileTriggerCommandWhy it matters
.github/workflows/atmos-pro.yamlpull_request, merge_groupatmos describe affected --uploadUploads affected stack/component pairs so Atmos Pro knows what to plan or apply.
.github/workflows/atmos-terraform-plan.yamlworkflow_dispatchatmos terraform plan --uploadRuns plans that Atmos Pro dispatches for affected stacks and drift checks.
.github/workflows/atmos-terraform-apply.yamlworkflow_dispatchatmos terraform deploy --uploadRuns approved applies with GitHub Environment protection.
.github/workflows/atmos-pro-upload-instances.yamlpush, schedule, workflow_dispatchatmos list instances --uploadUploads inventory so Stacks, Instances, and drift detection know what exists.
The affected stacks workflow is the entry point. It runs on pull request events and reports the result of atmos describe affected --upload to Atmos Pro. Atmos Pro then reads each affected component/stack pair, resolves dependencies from your stack configuration, and dispatches the configured plan or apply workflows.
The plan and apply workflows must be workflow_dispatch workflows because Atmos Pro calls them directly. Their workflow inputs must match the inputs configured under settings.pro in your Atmos stacks. At minimum, plan needs sha, component, and stack; apply needs those plus github_environment if you use GitHub deployment approvals.
The upload instances workflow is separate from affected stacks. It should run after changes merge to the default branch and on a schedule, because it keeps Atmos Pro's inventory current even when no pull request is open.
stacks/mixins/atmos-pro/default.yaml
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
This mixin assumes you use GitHub merge queue and keeps each lifecycle explicit: PR updates plan, the queue commit applies, and the merged PR lifecycle runs a post-merge plan/verification workflow. The command each lifecycle dispatches is your policy decision; the important rule is that every lifecycle that uploads affected stacks has matching settings.pro workflows. If your affected-stacks workflow uploads on pull_request.closed with merged == true, configure pull_request.merged. If that lifecycle should not run, skip atmos describe affected --upload for merged PR events instead. See Configure Atmos Stacks for the full lifecycle model.
For more detail on the stack configuration, see Configure Atmos Stacks. For the inventory workflow, see Upload Instances.

Don't use concurrency groups

Avoid adding concurrency: to workflows that Atmos Pro dispatches (plan, apply, upload instances, drift detection). By default GitHub Actions keeps one run executing and one run pending per concurrency group, and a third arrival replaces the pending run. The currently executing run is only cancelled when cancel-in-progress: true is set. GitHub also supports queue: max for FIFO queuing of up to 100 pending runs. We still recommend skipping concurrency: on Atmos Pro–dispatched workflows because Atmos Pro already serializes per stack+component, and layering GitHub-side concurrency on top creates confusing queue/cancel behavior and risks terraform plan/apply being interrupted — leaving state locks and partial work behind.
This workflow runs on every pull request event against the default branch (for example, main). It uses atmos describe affected --upload to determine which stacks changed and report them to Atmos Pro.
.github/workflows/atmos-pro.yaml
name: 👽 Atmos Pro
run-name: 👽 Atmos Pro
 
on:
  pull_request:
    types:
      - opened
      - synchronize
      - reopened
      - closed
    branches:
      - main
  merge_group:
 
permissions:
  id-token: write
  contents: read
  checks: write
 
jobs:
  affected:
    name: Trigger Affected Stacks
 
    runs-on:
      - "ubuntu-latest"
 
    container:
      image: ghcr.io/cloudposse/atmos:${{ vars.ATMOS_VERSION }}
 
    defaults:
      run:
        shell: bash
 
    if: |
      github.event_name == 'merge_group' ||
      (!contains(github.event.pull_request.labels.*.name, 'no-apply') &&
       (github.event.action != 'closed' || (github.event.action == 'closed' && github.event.pull_request.merged == true)))
 
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ github.event.pull_request.head.sha || github.sha }}
          fetch-depth: 0
 
      - name: Configure Git Safe Directory
        run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
 
      - name: Determine Affected Stacks
        env:
          ATMOS_PRO_WORKSPACE_ID: ${{ vars.ATMOS_PRO_WORKSPACE_ID }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          ATMOS_PROFILE: github
        run: |
          atmos describe affected --upload
The merge_group trigger lets Atmos Pro participate in GitHub's merge queue. When a PR enters the queue, GitHub fires merge_group once on the synthetic merge SHA in a gh-readonly-queue/<base>/pr-<N>-<sha> branch — atmos describe affected --upload re-runs against that SHA, and Atmos Pro reports a fresh "Atmos Pro" check on it. Without merge_group in this workflow's on: block, the queue's required-status-check enforcement on Atmos Pro will sit stuck on "Expected — Waiting for status to be reported".
Merge queue does not replace the merged PR lifecycle. With the closed trigger and if: guard below, this workflow also uploads affected stacks after the PR actually merges. That means settings.pro.pull_request.merged.workflows must exist for every component/stack instance that can be affected by the merged upload, or the workflow should skip atmos describe affected --upload for merged PR events.
The if: guard now also lets merge_group events through because github.event.pull_request is absent for queue runs, so the original PR-only condition would short-circuit them.
Notice that atmos describe affected --upload is called without --sha or --ref. With ci.enabled: true in your atmos.yaml, Atmos resolves the right base SHA per event (pull_request, pull_request_target, merge_group, push) from GITHUB_EVENT_NAME and GITHUB_EVENT_PATH. Older templates that pass --sha ${{ github.event.merge_group.base_sha }} or --sha ${{ github.event.pull_request.base.sha }} aren't wrong, just redundant — and they break the moment a new event type is added.

Scope uploads before dispatching broad lifecycles

Every affected component/stack reported by atmos describe affected --upload is evaluated against the matching lifecycle in settings.pro. If a queue or merge lifecycle dispatches apply, a PR that touches a defaults mixin imported by every stack can fan apply across every reported stack. Add a --stack filter for the lifecycle branch you want to constrain, or explicitly choose multi-stack dispatch. See Common pitfalls when enabling merge queue on the stack configuration page.
See GitHub Merge Queue in the stack configuration reference for the matching settings.pro.merge_group block.
Every workflow above pulls the Atmos container image at ghcr.io/cloudposse/atmos:${{ vars.ATMOS_VERSION }}. Set ATMOS_VERSION as a GitHub Actions variable so all workflows pin to the same release and you can roll forward in one place.

No leading `v`

Docker image tags don't use the v prefix. Set the value to 1.175.0 (or whichever release you want to pin), not vX.Y.Z — the container pull will 404 otherwise.
Run gh variable set from a checkout of the repo (or from anywhere with --repo). The snippets below have the latest Atmos release baked in — copy and run.
Repository scope:
gh variable set ATMOS_VERSION --body "1.175.0"
Organization scope — pin one Atmos version across every repo:
gh variable set ATMOS_VERSION --org my-org --visibility all --body "1.175.0"
Verify:
# Repository scope
gh variable get ATMOS_VERSION
 
# Organization scope
gh variable get ATMOS_VERSION --org my-org
All workflows use GitHub OIDC to authenticate with the Atmos Pro API, eliminating the need for long-lived API tokens. Workflows run wherever your GitHub Actions run, whether on GitHub-hosted runners or your own self-hosted runners.
Atmos uses Auth to manage how the CLI obtains cloud credentials. Because you typically have different identities depending on context — OIDC in CI versus single sign-on locally — we recommend using profiles. Profiles let you define separate auth configurations for each context (e.g., profiles/github/atmos.yaml for GitHub Actions, profiles/local/atmos.yaml for local development).
In your workflows, set the ATMOS_PROFILE environment variable to load the right profile:
env:
  ATMOS_PROFILE: github
Learn about authentication
The plan and apply workflows use workflow_dispatch so Atmos Pro can trigger them at the right time in the right order. Atmos Pro passes the following inputs:
sha (required)
The commit SHA to check out. Since workflow_dispatch doesn't have pull request context, Atmos Pro passes the SHA explicitly.
component (required)
The Atmos component to run the workflow on.
stack (required)
The Atmos stack to run the workflow on.
github_environment (required for apply)
The GitHub Environment to use for the workflow run. This ties into GitHub's native approval gates — configure environment protection rules to require manual approval before applies run. We recommend defining environments along permission boundaries, typically by stage (e.g., dev, staging, production), the same way you separate cloud accounts or roles. Atmos Pro reads this value from your stack configuration and passes it automatically.
Learn about ordered deploymentsLearn about deployment approvals
Each workflow YAML file must declare a permissions block so the GitHub Actions runner token has the access it needs:
In your workflow YAML file
permissions:
  id-token: write # Required for OIDC token generation
  contents: read # Required for checking out code
  checks: write # Required for creating/updating GitHub Checks
  statuses: write # Required for posting commit status contexts
The checks and statuses permissions are needed because the Atmos CLI writes status checks and commit statuses directly from these Atmos Pro workflow runs — for example, reporting how many resources a Terraform plan will create, update, or destroy. Atmos Pro orchestrates when workflows run, and each workflow reports the command result back through --upload.
The ATMOS_PRO_WORKSPACE_ID variable tells Atmos Pro which workspace a pull request or event belongs to. Set it as a GitHub Actions variable in your repository or organization settings. Your current workspace ID is ws_xxxxxxxxxxxxxxxxxxxxxxxxxxxx — you can always find this in the Workspace Settings page. The GITHUB_TOKEN is automatically provided by GitHub Actions.
Here's how it looks in your workflow YAML — reference the variable using vars.ATMOS_PRO_WORKSPACE_ID:
In your workflow YAML file
env:
  ATMOS_PRO_WORKSPACE_ID: ${{ vars.ATMOS_PRO_WORKSPACE_ID }}
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  ATMOS_PROFILE: github
Configure your Workspace ID
The env block in the workflows is a GitHub Actions workflow env block, not the Atmos env configuration section. It sets environment variables for the runner so the Atmos CLI can authenticate with Atmos Pro.
Atmos Pro uses the workflow filename to determine what command each deployment represents. This lets the dashboard show the correct badge and filter deployments by command type.
Recommended workflow filenames:
CommandFilenameDescription
Affectedatmos-pro.yamlRuns atmos describe affected --upload
Planatmos-terraform-plan.yamlRuns atmos terraform plan --upload
Applyatmos-terraform-apply.yamlRuns atmos terraform deploy --upload
Upload Instancesatmos-pro-upload-instances.yamlRuns atmos list instances --upload
Atmos Pro checks if the filename contains keywords like plan, apply, affected, or instances (case-insensitive). If the filename doesn't contain a recognizable keyword, the command is determined later when the Atmos CLI reports back the actual command that was executed.
The workflow filename is a best-effort heuristic. The canonical command comes from the Atmos CLI callback after the workflow runs. If the CLI reports a different command than what the filename suggests, the CLI value takes precedence.
You may have noticed how small these workflows are — that's by design. They are dedicated Atmos Pro entry points, and Atmos Native CI handles the supporting CI behavior around the commands. Out of the box, Native CI takes care of:
  • Toolchain installation — Atmos, Terraform/OpenTofu, and dependencies
  • Authentication — OIDC token exchanges and cloud credential management
  • Terraform backend provisioning — Automatic setup of state storage
  • Status checks — Reporting plan/apply results back to GitHub
  • Job summaries — Rich workflow run summaries in GitHub Actions
Learn about Atmos Native CI

Ready to get started?

Install the Atmos Pro GitHub App to get started, or explore a complete working example.

Create WorkspaceExplore Example Repository

GitHub RepositoryCommit From CI
Atmos Pro Logo

Atmos Pro

The fastest way to deploy your apps on AWS with Terraform and GitHub Actions.

GitHubTwitterLinkedInYouTubeSlack

For Developers

  • Quick Start
  • Example Workflows
  • Atmos Documentation

Community

  • Register for Office Hours
  • Join the Slack Community
  • Try our Newsletter

Company

  • About Cloud Posse
  • Security
  • Pricing
  • Blog
  • Media Kit

Legal

  • SaaS Agreement
  • Terms of Use
  • Privacy Policy
  • Disclaimer
  • Cookie Policy

© 2026 Cloud Posse, LLC. All rights reserved.

Checking status...