Skip to content
Infrastructure as Code iac cicd 4 min read

Atlantis

Atlantis is a self-hosted application that runs Terraform plan and apply from your pull requests. Instead of running Terraform locally and pasting plan output into a PR, your team comments atlantis plan and atlantis apply directly on the PR, and Atlantis posts the results back as comments. This keeps every infrastructure change reviewable, auditable, and gated behind your normal code-review process — which is why platform teams adopt it for collaborative Terraform at scale.

How Atlantis works

Atlantis runs as a long-lived server (a container, an EC2 instance, or a Kubernetes deployment) that receives webhooks from your VCS provider — GitHub, GitLab, Bitbucket, or Azure DevOps. When a pull request touches Terraform files, Atlantis clones the branch, detects affected projects, and runs a plan automatically. Reviewers see the exact plan output inline, approve the PR, then trigger the apply with a comment. Because Atlantis executes Terraform server-side, the credentials never leave your infrastructure and developers don’t need cloud admin access on their laptops.

The core loop is:

  1. A developer opens a PR that modifies .tf files.
  2. Atlantis receives the webhook and runs terraform plan, posting the output as a PR comment.
  3. Atlantis locks the affected directory/workspace so no other PR can plan or apply it concurrently.
  4. Reviewers approve. The author comments atlantis apply.
  5. Atlantis runs terraform apply, posts the result, and releases the lock when the PR is merged or closed.

Atlantis is fully compatible with OpenTofu — set --default-tf-distribution=opentofu (or default_tf_distribution: opentofu in server config) and Atlantis will run tofu instead of terraform with identical comment commands.

PR comment commands

You interact with Atlantis entirely through PR comments. The most common commands:

CommandPurpose
atlantis planPlan all changed projects in the PR
atlantis plan -d infra/prodPlan a specific directory
atlantis plan -p networkPlan a named project from atlantis.yaml
atlantis applyApply all planned projects (requires approval)
atlantis apply -d infra/prodApply a single directory
atlantis unlockManually release locks held by the PR
atlantis helpList available commands

Configuring a repository

Project-level behavior is defined in an atlantis.yaml file at the repo root. This is where you declare projects, autoplan rules, and custom workflows.

version: 3
automerge: true
projects:
  - name: network
    dir: infra/network
    workspace: default
    terraform_version: v1.9.5
    autoplan:
      when_modified: ["*.tf", "../modules/**/*.tf"]
      enabled: true
    apply_requirements: [approved, mergeable]
  - name: prod
    dir: infra/prod
    terraform_version: v1.9.5
    apply_requirements: [approved, mergeable, undiverged]

The apply_requirements field enforces guardrails: approved blocks apply until a reviewer approves, mergeable blocks until CI checks pass, and undiverged blocks if the branch is behind the base.

The Terraform code itself is unchanged — Atlantis runs your normal configuration. A typical project might manage an S3 backend and a bucket:

terraform {
  required_version = ">= 1.5"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  backend "s3" {
    bucket         = "devcraftly-tfstate"
    key            = "prod/network.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  tags = {
    Name = "prod-vpc"
  }
}

Example PR flow

When the PR opens, Atlantis autoplans and comments back:

Output:

Ran Plan for project: network dir: infra/network workspace: default

Terraform will perform the following actions:

  # aws_vpc.main will be created
  + resource "aws_vpc" "main" {
      + cidr_block           = "10.0.0.0/16"
      + enable_dns_hostnames = true
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

To apply this plan, comment:
  atlantis apply -d infra/network

After approval, the author comments atlantis apply:

Output:

Ran Apply for project: network dir: infra/network workspace: default

aws_vpc.main: Creating...
aws_vpc.main: Creation complete after 2s [id=vpc-0a1b2c3d4e5f6a7b8]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Locking and concurrency

Atlantis maintains its own directory/workspace locks separate from your Terraform state lock. When a PR plans infra/prod, that path is locked until the PR is applied and merged or the lock is released. A second PR touching the same path will be told the project is locked and shown a link to the blocking PR. This prevents the classic race where two engineers apply conflicting plans against the same state. State-file locking (via the DynamoDB table above) remains a second, lower-level safety net.

Running the server

You can run Atlantis from its official image. It needs VCS credentials and a webhook secret to validate incoming payloads.

docker run -p 4141:4141 ghcr.io/runatlantis/atlantis:latest \
  server \
  --gh-user="$ATLANTIS_GH_USER" \
  --gh-token="$ATLANTIS_GH_TOKEN" \
  --gh-webhook-secret="$ATLANTIS_WEBHOOK_SECRET" \
  --repo-allowlist="github.com/devcraftly/*"

Always set --repo-allowlist to an explicit pattern. Without it Atlantis will refuse to start, and a too-broad allowlist (*) lets any repo trigger Terraform runs against your cloud credentials.

Best practices

  • Set apply_requirements: [approved, mergeable] (and undiverged for production) so no infrastructure changes apply without review and green CI.
  • Scope --repo-allowlist narrowly and store the webhook secret so Atlantis rejects forged payloads.
  • Use autoplan.when_modified to include shared module paths, so a module change replans every dependent project.
  • Pin terraform_version (or the OpenTofu equivalent) per project to keep plans deterministic across upgrades.
  • Give Atlantis a dedicated, least-privilege cloud role rather than reusing a developer’s credentials.
  • Enable automerge only once your apply_requirements and CI gates are trustworthy, to avoid auto-merging unreviewed changes.
Last updated June 14, 2026
Was this helpful?