Atlantis runs terraform plan and apply on a server that holds credentials to your cloud accounts, so anyone who can open or comment on a pull request it trusts can, in effect, run code next to those credentials. This guide covers the customer side of running Atlantis safely: which repositories and webhooks it accepts, who may run which commands, how to keep a malicious plan from reaching cloud credentials, how to scope the Git host token and provider credentials, and what to log. It is written for platform engineers, DevOps teams and security reviewers who operate a self hosted Atlantis server. For pipeline credential patterns that apply to any Terraform automation, see the Terraform and IaC Pipeline Credential Security Guide.
This is an independent guide from NHI Mgmt Group, based on Atlantis's public documentation as of November 2026. It is not sponsored or reviewed by the Atlantis project. Product features change, so confirm settings against Atlantis's current documentation.
Key takeaways
- Treat plan as code execution. The Atlantis docs state that a malicious provider or an
externaldata source can run code duringterraform plan, so apply approvals alone do not protect credentials. - Accept only what you own. Set a narrow
--repo-allowlist, a webhook secret of more than 24 random characters and HTTPS, and leave--allow-fork-prsat its default of false. - Control behaviour from the server side. The repo level
atlantis.yamlis read from the pull request branch, so keep workflows, overrides and requirements in the server siderepos.yaml. - Gate apply by people and policy. Use
apply_requirements: [approved, mergeable],--gh-team-allowlistor ateam_authzcommand, and Conftest policy checks with named policy owners. - Scope the credentials Atlantis holds. Prefer cloud workload roles over static keys, separate roles per environment, restrict egress for provider downloads, and protect the web UI and API with their own secrets.
How identity and access work in Atlantis
Atlantis has no user directory of its own. It listens for webhooks from your Git host (GitHub, GitLab, Bitbucket, Azure DevOps or Gitea), reacts to pull request events and comments such as atlantis plan and atlantis apply, and posts results back using a Git host identity. Who may trigger work is decided by four layers: which repositories the server accepts, whether a webhook is genuine, who the Git host says the commenter is, and what the server side configuration allows that person to do.
| Layer | Setting | Documented default | Customer action |
|---|---|---|---|
| Repository scope | --repo-allowlist | Required flag; * accepts every repository | List your organisation or named repositories; use ! entries to exclude untrusted repositories |
| Webhook authenticity | ATLANTIS_GH_WEBHOOK_SECRET, ATLANTIS_GITLAB_WEBHOOK_SECRET; Basic auth for Azure DevOps | Optional | Always set; more than 24 random characters; same secret on each repository webhook |
| Transport | --ssl-cert-file, --ssl-key-file or a TLS terminating proxy | HTTP unless configured | Serve HTTPS so the webhook secret cannot be read in transit |
| Fork pull requests | --allow-fork-prs | false | Keep false; Atlantis also only runs for pull requests whose head repository is on the same host |
| Who can run commands | --gh-team-allowlist or team_authz | Any team can plan and apply | Restrict apply and import to named teams |
| Which commands exist | --allow-commands | version,plan,apply,unlock,approve_policies,cancel | Add import and state only where needed; avoid all |
Atlantis checks authorisation twice: once for the repository as a whole after a command is validated, and again per project after pre workflow hooks run and affected projects are known. An external team_authz command receives the command name, repository and the user's teams, plus variables such as PROJECT_NAME, BASE_BRANCH_NAME and PULL_AUTHOR, and must print pass on its last line with exit code 0 to allow the action. The two options are mutually exclusive: when team_authz is defined, --gh-team-allowlist is ignored.
Approval semantics depend on the Git host
The approved requirement means at least one approval by someone other than the author, and the Atlantis docs note that on GitHub any user with read permission can approve. Pair approved with mergeable, so that branch protection rules (required reviewers, CODEOWNERS, status checks) decide whether an apply may run. Atlantis also offers undiverged, which requires project files to be ahead of the base branch, and the current docs state that atlantis state rm must satisfy both apply_requirements and import_requirements.
Admin roles and privileged access
The most privileged roles in an Atlantis deployment are not inside Atlantis: they are the people who can change the server's flags and repos.yaml, read its environment, or exec into its container. Treat the Atlantis host, its Helm values or task definition, and its data directory as tier zero for every cloud account it can change.
- Server configuration owners: keep
repos.yaml, server flags and policy sets in a separate, protected repository with its own reviewers. A change there can relax every guard on this page. - Repo level overrides:
allowed_overrideslists which keys a repository's ownatlantis.yamlmay change (for exampleapply_requirements,workflow,repo_locks,policy_check). Grant overrides per repository ID, not to/.*/, and leaveallow_custom_workflowsfalse unless a repository genuinely needs its own steps. - Custom workflows and hooks: custom
runsteps andpre_workflow_hooksexecute shell commands on the server. Define them only in server side configuration and useallowed_workflowsto name the ones a repository may select. - Policy owners: under
policies.owners, list the users or teams who may runatlantis approve_policiesto override a failed policy. Keep this group small and distinct from day to day committers. - Host access: restrict SSH,
kubectl execand ECS Exec on the Atlantis workload to the platform team. The Atlantis docs note that if you can exec into the server and run Terraform, Atlantis will work; the same is true for anyone else with that access.
| Control point | Where it lives | Who should hold it |
|---|---|---|
| Server flags and environment | Helm values, Kubernetes Secret, ECS task definition or systemd unit | Platform team, changes by pull request |
repos.yaml and workflows | Server side config file, ideally baked into the image or mounted read only | Platform and security reviewers |
| Policy sets and owners | policies block in repos.yaml | Security or cloud governance team |
| Production apply | --gh-team-allowlist or team_authz plus branch protection | Named deployers per environment |
| Locks and unlock | Web UI and atlantis unlock | Authenticated operators only |
Integrations, API tokens and service accounts
An Atlantis server holds several non-human credentials. Inventory each one, give it a named owner and record where it is stored. See the Secrets Management Guide for storage patterns.
| Credential | Purpose | Scope guidance from the docs | Our recommendation |
|---|---|---|---|
| Git host token or GitHub App | Clone, comment, set commit statuses | GitHub token with repo scope, or a GitHub App whose permissions are set at creation; GitLab token with api scope; Bitbucket Cloud token with Read project and Write pull request permissions | Prefer a GitHub App installed only on allowlisted repositories; use a dedicated bot account, never a person's token |
| Webhook secret | Prove webhooks came from the Git host | More than 24 characters; same secret for each repository | Generate randomly, store in a secret manager, rotate on staff changes |
| Web UI credentials | Basic auth on the locks and jobs UI | --web-basic-auth with --web-username and --web-password (password default is atlantis) | Set a long random password, or front the UI with an SSO proxy |
| API secret | Authenticate /api/* calls via the X-Atlantis-Token header | --api-secret; the API is experimental and gated by the api or drift-detection experiment | Enable only when used; store and rotate like any API key |
| Cloud provider credentials | Terraform plan and apply | Instance roles, GCE service accounts, environment variables, credential files or the Vault provider | Use workload roles; avoid long lived access keys in environment variables |
GitHub App permissions after upgrades
The Atlantis docs record that new GitHub App permissions were added over time (Administration from v0.19.7, Members from v0.22.3 for team based features, Actions from v0.30.0 for mergeable checks) and that upgrades do not add them automatically. When you grant a new permission, record why, and review the App's permission list after each upgrade so it stays at what your enabled features need.
Provider credentials by environment
Atlantis passes whatever credentials the host has to every Terraform run. To limit blast radius, run separate Atlantis instances (or separate workloads) per trust boundary, such as one for production accounts and one for non production. With AWS, the docs describe using a credentials file whose default profile can assume each required role; use assume role session names that include the Atlantis user, repository and pull request number so CloudTrail shows who triggered each change. Keep state backend credentials as tightly scoped as the provider role, because state files hold resource attributes that can include secrets. See the Cloud Workload Identity Guide for role based patterns.
Protecting against malicious plans
The Atlantis security page lists the main ways a server can be misused: a pull request containing a malicious provider or external data source that runs during an automatic plan, a local-exec provisioner that runs on apply, malicious custom build commands in an atlantis.yaml taken from the pull request branch, and unwanted atlantis plan or apply comments. The defences below come from that page and the server configuration reference.
- Control provider sources. Bake approved providers into the Atlantis image or host and deny public egress in production, or run an internal provider registry so you decide who can publish to it.
- Validate before plan. Add steps to the server side
planworkflow that reject disallowed providers, data sources or authors; the docs suggest Conftest for this and even requiring a reaction on the pull request before plan continues. - Limit autoplan where risk is high.
--disable-autoplanor--disable-autoplan-labelstop plans running on every push, so a human chooses when untrusted changes are planned. - Restrict variable files. Keep
--var-file-allowlistat its default (the Atlantis data directory) or a named directory, so comments such asatlantis plan -- -var-file=...cannot read arbitrary server files. - Require explicit project applies.
--disable-apply-allforces each apply to name a project, workspace or directory. - Check plans with policy. Enable
--enable-policy-checks, definepolicy_setsand owners, and addpolicies_passedto apply requirements. If you use--gh-team-allowlist, allowlist the internalpolicy_checkcommand too, or manual plans will skip policy checks.
Policy checks run against plan output, which is after plan code has executed, so they complement egress and provider controls rather than replace them. See the OWASP CI/CD Top 10 CICD-SEC-4 Poisoned Pipeline Execution Guide for the wider pattern.
Logging and detection
Atlantis activity is spread across three places: the Git host (pull request comments, approvals, webhook deliveries), the Atlantis server (logs and metrics) and the cloud provider (API calls made by Terraform). Correlate them by pull request number.
| Source | What to collect | What to alert on |
|---|---|---|
| Atlantis server logs | Run at --log-level=info and ship to your SIEM | Webhook validation failures, commands from unexpected users, unlocks, policy approvals |
| Atlantis metrics | Prometheus or StatsD via the metrics block in server side config | Spikes in command errors or execution counts outside change windows |
| Git host audit log | Webhook changes, App installation and permission changes, branch protection edits | Webhook secret changed or removed; App installed on new repositories |
| Pull request comments | All atlantis commands and who issued them | apply, import or state on production projects by people outside the deployer group |
| Cloud audit logs | CloudTrail, Azure Activity Log or Cloud Audit Logs for the Atlantis role | Calls from the Atlantis role outside a running job, or from unexpected networks |
Metrics to track (our suggestions): share of allowlisted repositories with a webhook secret (target 100 percent); production applies with an approval and passing policy checks (target 100 percent); policy overrides per month and who approved them; age of the Git host token and webhook secret; number of static cloud keys on Atlantis hosts (target zero).
Hardening baseline
A phased rollout keeps teams working while the controls tighten. Test each phase on a non production Atlantis instance first; every step below can be rolled back by reverting the server configuration pull request.
- Phase 1, perimeter: set
--repo-allowlistto named repositories, set the webhook secret, serve HTTPS, keep--allow-fork-prsfalse, and turn on--web-basic-authwith a strong password or an SSO proxy. Keep the server off the public internet except for webhook delivery, and restrict that path to your Git host's published address ranges where you can. - Phase 2, who can do what: move workflows and requirements into
repos.yaml, setapply_requirements: [approved, mergeable], restrict apply with--gh-team-allowlistorteam_authz, and limitallowed_overrides. - Phase 3, credentials: replace static keys with workload roles, split production and non production, add session names for traceability and store every secret in a secret manager rather than plain flags.
- Phase 4, plan safety: restrict egress, bake or mirror providers, add pre plan validation and enable Conftest policy checks with owners. Start policies as warnings in non production, then enforce.
- Phase 5, operate: forward logs and metrics, review App permissions after every upgrade, and apply Atlantis releases promptly using the project's security advisories.
Decision guidance
- Small team, private repositories: one instance, organisation wide allowlist, team allowlist for apply and policy checks on production projects is a sound baseline.
- Large or regulated estate: separate instances per environment or business unit,
team_authzfor per project rules, a private provider registry and egress controls. - Public repositories: the Atlantis docs advise against running on public repositories, because anyone can comment; if you must, keep forks disabled, disable autoplan and require approval before any plan.
Common misconfigurations
- Wildcard allowlist without a webhook secret:
--repo-allowlist=*is only reasonable in a protected network and, per the docs, dangerous without a webhook secret, because anyone who reaches the endpoint can pose as an allowlisted repository. - Trusting repo level configuration: allowing
allow_custom_workflows: trueor broadallowed_overridesfor every repository lets a pull request change its own build steps, sinceatlantis.yamlis read from the pull request branch. - Relying on apply approval alone: plan time code execution reaches credentials before anyone approves an apply; add egress and provider controls.
- One all powerful cloud identity: a single role with administrator rights across every account turns any accepted pull request into full estate access.
- Default web credentials: leaving basic auth off, or the password at its documented default, exposes locks and job output that may include plan details.
- Team allowlist without
policy_check: manual plans then skip policy checks while autoplans still run them, which hides gaps.
Framework mapping
| Control area | NIST SP 800-53 Rev 5 | MITRE ATT&CK risk addressed | OWASP CI/CD Top 10 |
|---|---|---|---|
| Repository allowlist, fork controls, pre plan validation | CM-3 Configuration Change Control, SA-10 Developer Configuration Management | T1195.002 Compromise Software Supply Chain | CICD-SEC-4 Poisoned Pipeline Execution |
| Team allowlist, apply requirements, policy owners | AC-6 Least Privilege | T1078.004 Cloud Accounts | CICD-SEC-1 Insufficient Flow Control Mechanisms |
| Webhook secret, Git host token, cloud roles | IA-5 Authenticator Management | T1552.001 Credentials In Files | CICD-SEC-6 Insufficient Credential Hygiene |
| Provider egress control and private registry | SR-3 Supply Chain Controls and Processes, CM-7 Least Functionality | T1195.002 Compromise Software Supply Chain | CICD-SEC-3 Dependency Chain Abuse |
| Server logs, metrics, cloud audit | AU-2 Event Logging | T1078.004 Cloud Accounts | CICD-SEC-10 Insufficient Logging and Visibility |
Practitioner checklist
- Set
--repo-allowlistto your organisation or named repositories, with!exclusions for untrusted ones. - Configure a random webhook secret of more than 24 characters on every repository and serve Atlantis over HTTPS.
- Keep
--allow-fork-prsfalse and do not run Atlantis on public repositories. - Define workflows, requirements and
allowed_overridesin server siderepos.yaml; keepallow_custom_workflowsoff by default. - Require
approvedandmergeablefor apply, import and state commands, backed by branch protection. - Restrict apply to named teams with
--gh-team-allowlistorteam_authz, and allowlistpolicy_checkwhen using team lists. - Enable Conftest policy checks with a small owner group and add
policies_passedfor production. - Replace static cloud keys with workload roles, separated by environment, with traceable session names.
- Restrict egress and source providers from a baked image or private registry.
- Enable web basic auth with a strong password or SSO proxy, and set
--api-secretonly when the API is used. - Forward server logs, metrics and cloud audit events, and review GitHub App permissions after each upgrade.
Standards and references
- Atlantis: Security
- Atlantis: Server Configuration
- Atlantis: Server Side Repo Config
- Atlantis: Repo and Project Permissions
- Atlantis: Command Requirements
- Atlantis: Conftest Policy Checking
- Atlantis: Provider Credentials
- OWASP: Top 10 CI/CD Security Risks
Related NHI Mgmt Group resources: Infrastructure as Code Security Guide, Terraform and IaC Pipeline Credential Security Guide, Webhook Secrets Security Guide, CI/CD Pipeline Identity Security Guide, Policy-as-Code for Authorisation Guide.