Atlantis at Scale: How It Works and Where It Breaks

Source: Spacelift•

Atlantis at Scale: How It Works and Where It Breaks

Running Atlantis at scale: architecture patterns, limitations, and how it compares to alternatives like Spacelift.

Atlantis is the tool of choice when engineers run terraform apply from their laptops. You drop a server in your network, wire up webhooks, and then your infrastructure changes are applied via pull requests like normal code.

That story holds up really well for a while, but when your organization grows, your repo count expands, and multiple people open PRs at the same time, things start feeling slow and fragile. In this article, we will walk through what Atlantis is, where it starts to break as you scale, what a serious production deployment looks like, and when it makes sense to move to something else.

What we’ll cover:

  • What is Atlantis?
  • Where Atlantis breaks down at scale
  • How to deploy Atlantis at scale
  • Why use Spacelift as an Atlantis alternative
  • Atlantis puts terraform plan and apply inside your pull requests, and it holds up until every clone, init, plan, and apply is queueing behind one server.
  • You can push past that with Redis locking, S3 plan storage, team allowlists, and the alpha drift API, but the scheduler, the permission script, and the availability work are yours to build and keep running.
  • Spacelift gives you the same pull request workflow with drift detection, policy as code, stack dependencies, and real role-based access control already built in.

Atlantis is an open-source tool that automates Terraform through pull requests. It is a single GO binary that listens for webhooks from your Git provider, runs Terraform commands, and posts the output back as a comment on the PR.

The process is simple and easy to follow:

  • You modify your Terraform files and open a pull request.
  • Once the pull request is open, Atlantis receives a webhook, clones the repo, and runs terraform plan.
  • The plan output appears as a comment on your PR.
  • A reviewer approves, someone comments atlantis apply, and Atlantis runs the apply.
  • The PR merges, and Atlantis unlocks the project.

The Atlantis adoption is really simple, as everything happens where your team already works. You don’t need to learn a new UI; just read a comment and type another one.

Atlantis is currently a CNCF project (sandbox maturity level), with v0.47.1 as the latest tagged release and a 1.0.0 still on the roadmap.

Atlantis architecture overview at scale

Of the many ways to deploy Atlantis, the most common is Kubernetes. By default, Atlantis has no external database and stores plan files on the local disk. The official Kubernetes manifests use a StatefulSet with replicas: 1 and a persistent volume.

So, the Atlantis architecture has:

  • One HTTP server that accepts webhooks and serves a small web UI
  • A working directory on disk where every repo gets cloned per PR and workspace
  • A locking layer that helps prevent two PRs from planning or applying to the same project at the same time.
  • Terraform binaries that Atlantis uses to download and execute as subprocesses on the same machine

Look at that list again, and consider a scenario in which you have tens of configurations: Every provider plugin download, every clone, every plan, and every apply happens on one box, sharing CPU, memory, disk, and network. Atlantis does the work in-line because there is no queue, you can drain across nodes, and no scheduler decides where work runs.

Let’s explore some failure modes you will actually hit, roughly in the order most teams hit them.

1. The single server becomes a queue

Atlantis runs every clone, plan, and apply as a subprocess on the machine that received the webhook. That is fine when two people are working.

It stops being fine when your monorepo has 70 projects and a PR touches a shared module because autoplan fires for every affected project. Multiply that by four open PRs and the worker pool is saturated. The plan comment lands 20 minutes late, someone pings you in Slack to ask whether Atlantis is down, and you spend the afternoon confirming the server was fine and just busy.

A second PR touching the same directory doesn’t quietly wait its turn either. It gets a lock error pointing at the PR that holds the lock, and a person has to clear it.

2. Vertical scaling is the only straightforward lever you have

The obvious fix is a bigger box, and it works for a while. You add CPU and memory and then raise --parallel-pool-size, which controls how many plans and applies run at once and defaults to 15 on a single machine.

The catch is what actually saturates first. Teams end up on very large instances with the CPU mostly idle and the disk thrashing on git clones and provider downloads, paying for Monday morning capacity every day of the month.

Raising the pool size without adding disk and network headroom moves the contention rather than removing it.

3. You can scale horizontally, but you have to opt into it

By default, Atlantis stores locks in an embedded database only one process can open, which is what kept it to a single replica for years. Switching to --locking-db-type=redis lets several replicas share lock state.

Plan files still sit on local disk, so the replica that receives the atlantis apply webhook may have nothing to apply. The fix for that is external plan storage. Turn on --enable-external-stores, point it at an S3-compatible bucket, and Atlantis uploads plans after a plan and restores them wherever the apply lands.

Redis plus object storage is the documented path to stateless high availability, and it works. It also adds two more pieces of infrastructure that you run, secure, monitor, and upgrade next to Atlantis.

4. Provider and module downloads dominate runtime

Every run gets a fresh working directory per PR and workspace, so terraform init runs again and providers download again.

Atlantis does cache plugins by default, but the documentation recommends turning that cache off when several projects plan and apply in the same PR because Terraform has a known race condition on a shared cache directory. The two things you want most under load, parallelism and caching, pull against each other.

At small volume this costs seconds, but across dozens of projects a day, it becomes most of your run duration and a visible line on your egress bill.

5. Access control is too limited

Atlantis gives you two ways to decide who can run what. The first is --gh-team-allowlist, which maps GitHub teams to commands globally, so you can say this team applies and everyone else plans.

The second is an external authorization command. Atlantis passes it the command, the repo, the project, and the user’s teams, and your script answers pass or fail. That script is a piece of production access control that you write, test, and maintain forever.

The product has no roles, no environments, and no per-account model. A rule like “the data team can apply to their own AWS account and nowhere else” becomes either that script or a separate Atlantis deployment with its own credentials.

6. Every credential ends up on the same box

If Atlantis manages infrastructure across ten AWS accounts, that one server holds access to all ten. It is the most privileged machine you run, the one with the widest blast radius if someone compromises it, and the one your platform team patches by hand.

Splitting deployments by blast radius is the standard answer, and it does contain the risk. It also means you now operate four Atlantis servers, each with its own configuration, credentials, webhooks, and upgrade schedule.

7. There is a simple alpha version of drift detection

Atlantis only sees the changes that arrive through a PR. When somebody clicks in the AWS console, Atlantis has no idea until the next plan on that project, which can be weeks away.

As of v0.45.0 there is an alpha drift API. Turn on --enable-drift-detection and you get endpoints to trigger a scan, read the cached result, and remediate, plus webhooks that can post to Slack when drift shows up.

However, you don’t get a scheduler. Something outside Atlantis has to call that endpoint on a cadence and you own it, automatic remediation needs a second flag on top, and because the API is alpha its request and response shapes can change between releases.

8. No dependency graph across projects

If your networking stack has to be applied before your EKS stack, Atlantis will not enforce that. Inside a single PR you can group projects into execution orders, which covers the case where one change touches both.

Across PRs and repositories there is nothing. No graph, no shared outputs, no way to declare that one project depends on another.

The ordering ends up in a runbook, a wrapper script, or one engineer’s head, and you find out it was wrong when an apply fails.

9. Visibility is limited to PR comments

Atlantis ships a small web UI for locks and streams logs live while a run is in flight, which is genuinely useful in the moment.

After the PR closes, your audit trail is a set of comments spread across repos, some collapsed, some edited. Answering “who applied this, when, and what did the plan actually say” means digging through pull requests one at a time.

There is no central run history, no searchable log store, and no per-team dashboard unless you build one and keep it running.

The true cost of running Atlantis at scale

Atlantis is free to download, but it is not free to run. You should evaluate what you are actually paying for:

  • Compute and storage – You will always have one or multiple servers capacity-sized for peak concurrency, along with persistent volumes and Redis.
  • Availability work: A StatefulSet with one replica is a single point of failure. Making it highly available is a project.
  • Platform engineering time – You need at least one permanent engineer to handle upgrades, webhook debugging, custom workflows, credential rotation, plugin caching, and the wrapper scripts around Terragrunt or multi-account setups.
  • Developer waiting time – Having 20 engineers waiting an extra ten minutes per plan, several times a day is far more expensive than any instance type.
  • Risk – A lack of drift detection and policy enforcement means mistakes get found in production instead of in a plan.

Before deciding Atlantis is too expensive, measure your actual numbers. You can track the time from PR open to plan comment and also count how many times a week someone asks about a stuck run.

If you decide to stay on Atlantis, here are some decisions that will help your deployment scale and survive growth:

  • Decide centrally, not per repo: Point the server to a central repo config with --repo-config and set your defaults there: which workflow runs, which requirements must pass before an apply, and how much a repo can override. Otherwise, every team decides its own rules, and nobody owns the result.
  • Cache your provider downloads: Terraform downloading plugins involves a big chunk of runtime. The best practice here is to set a plugin cache directory and back it with persistent storage.
  • Set concurrency based on reality: The number of plans and applies that run at once is controlled by --parallel-pool-size, which defaults to 15 and applies to a single machine. Raising it without adding CPU and memory simply moves the queue rather than removing it.
  • Turn off the dangerous shortcuts: Someone could comment atlantis apply with no arguments and apply every project in the PR at once. Using --disable-apply-all forces them to name what they are applying, and it costs your engineers three seconds.
  • Watch what triggers a plan: Tighten your autoplan file patterns so a docs change doesn’t trigger 20 runs. Narrow the patterns per project and your server stops doing work nobody asked for.
  • Keep the disk on Kubernetes: Plans live on local disk, so the official manifests use a StatefulSet with a persistent volume, and without it, a restart wipes every plan in flight. Scaling beyond a single replica adds Redis-based locking and requires either a shared filesystem or external plan storage.

At some point, you may start asking yourself, “Do I want to keep building a platform or just use one?” Spacelift is an alternative to Atlantis because it offers the same PR workflow. In addition to supporting Terraform/OpenTofu and Terragrunt, Spacelift also supports CloudFormation, Pulumi, Kubernetes, and Ansible.

The things that you would have to build around Atlantis are already there:

  • Policy as code with OPA can be used at multiple decision points, not only at apply time. You can control what resources users can create, what parameters they can have, how many approvals you need for a run, where to send notifications, what happens when a pull request is open or merged, and more.
  • Built-in drift detection helps you understand the changes that were made to your stacks on a schedule that you define, but you don’t need to configure any special scheduler for that, as this is built-in.
  • Stack dependencies let you create dependencies between your configurations and even share outputs between them. This works cross-tools as well, so you can easily implement Terraform+Ansible workflows, or even Terraform+Kubernetes ones. These dependencies are a directed acyclic graph, and they can go on as many nested levels as you want.
  • Real RBAC and SSO with SAML 2.0, which gives you transparency into who can apply what and where. This is a config setting, not a separate deployment per team.
  • Managed state with credentials scoped to a single run, or your own backend if you prefer to host the state yourself.
  • Private worker pools that run inside your network when you need them, plus a private module registry with version testing.
  • Full run history and audit trail in one place, instead of scattered across PR comments.
  • Self-service infrastructure through Blueprints and Templates. Blueprints can automate everything that is related to your stacks, from VCS config, the tools you are using, lifecycle hooks, and more, and they even integrate natively with ServiceNow and Backstage. At the same time, you can also use Templates, which give you immutable, self-service infrastructure.
  • AI support is built in through Spacelift Intelligence: You have an AI assistant in the product that can help you understand, at a glance, the status of your Spacelift resources, and also help you generate new ones. In addition to that, you can also use Spacelift Intent to generate infrastructure through natural language, which is also tracked in the same way as you would your normal infrastructure. This is extremely useful in development and testing environments when developers need to create something fast and easy, without going through your platform engineering teams.

With Spacelift, you stop maintaining the server itself, which minimizes your platform team’s workload.

You can see a more detailed comparison between the products here. If you’d rather watch, check out the video below:

How to migrate from Atlantis to Spacelift

The migration process is straightforward, as your Terraform code does not change.

  • Each Atlantis project needs its own stack. Everything listed under projects: in atlantis.yaml becomes a Spacelift stack pointing at the same repo, branch, and directory. If you have dozens, define them with the Spacelift Terraform provider so the whole setup is code you can review and version.
  • Import your state. If you want Spacelift to manage your state, import your existing state file during stack creation, or from the state history tab on an existing stack. Also, you should remove the backend block from your Terraform code entirely because Spacelift injects its own configuration at runtime, and an existing backend will break init. If you want to keep your S3 backend, you can, and then nothing changes here.
  • Move your credentials. You can swap the static cloud credentials from your Atlantis server for Spacelift’s cloud integrations.
  • Translate your rules into policies. Your apply_requirements become approval policies, while the ad hoc rules reviewers previously applied manually become plan policies that automatically fail a run when something is off.
  • Run both in parallel briefly. First, you should point a couple of low-risk stacks at Spacelift and keep Atlantis on everything else. Then compare plan output on the same PR until you trust it.
  • Turn off autoplan in Atlantis, disable manual access, remove webhooks, and revoke credentials, repo by repo, then decommission the server once the last stack has moved.

Spacelift also has a migration tool called liftoff that currently supports Terraform Cloud and Terraform Enterprise, with other sources planned.

Most teams migrate from Atlantis to Spacelift over a few weeks rather than a big weekend cutover.

Atlantis is an excellent tool with a simple design: one server, local disk, and PR comments as the interface. That simplicity is exactly why it is so easy to start with. Its limits are structural: single-server execution, disk-bound plan files, coarse permissions, no enterprise-ready drift detection, and no dependency graph.

You can scale it with Redis locking, plugin caches, tuned parallel pools, server-side repo config, and split deployments per blast radius. You should always measure the real cost before deciding to use Atlantis and plan latency and platform engineering hours matter far more than the instance bill.

If you need an enterprise-ready alternative, book a demo with one of our engineers to learn more about how Spacelift can help.

What this article says