Autoplan#
Autoplan is the mechanism that automatically runs terraform plan on a pull request when it is opened or a new commit is pushed. It determines which projects to plan by matching changed files against configurable glob patterns, and provides several ways to suppress or disable the behavior.
Webhook Triggering#
All supported VCS providers converge on a single code path. The VCSEventsController.Post() method routes incoming webhooks to provider-specific handlers, each of which calls handlePullRequestEvent() .
Provider handlers:
- GitHub —
handleGithubPost() - GitLab —
handleGitlabPost() - Bitbucket Cloud / Server —
handleBitbucketCloudPost()/handleBitbucketServerPost() - Azure DevOps —
handleAzureDevopsPost() - Gitea —
handleGiteaPost()
handlePullRequestEvent() fires RunAutoplanCommand() asynchronously (via goroutine) on OpenedPullEvent or UpdatedPullEvent only — closed/merged events do not trigger autoplan .
Bitbucket note: Bitbucket lacks a webhook that fires only on new commits. Atlantis caches the last commit SHA to avoid duplicate plans. If the cache is cleared, a spurious plan may occur. (runatlantis.io docs)
Execution Flow#
RunAutoplanCommand() in command_runner.go orchestrates the full autoplan lifecycle:
- Drainer check (graceful shutdown guard)
- Team permission check
- Build
command.ContextwithTrigger: command.AutoTrigger - Validate context (fork rules, branch rules)
- Check
DisableAutoplanglobal flag - Check
DisableAutoplanLabelper-PR label - Run pre-workflow hooks
- Invoke
PlanCommandRunner.Run()→runAutoplan()(triggered whenctx.Trigger == command.AutoTrigger) - Run post-workflow hooks
Disable / Suppression Mechanisms#
| Mechanism | Config | Scope |
|---|---|---|
| Global disable | --disable-autoplan / DisableAutoplan: true | All PRs |
| Label-based | --disable-autoplan-label <label> | Per PR |
| Per-project | autoplan.enabled: false in atlantis.yaml | Per project |
Label-based suppression : when DisableAutoplanLabel is set, Atlantis fetches the PR's labels via the VCS API before running. If the label is present, autoplan is skipped with a log message. On label-fetch error, autoplan proceeds (fail-open).
File Pattern Matching (when_modified)#
Without atlantis.yaml#
Atlantis uses DefaultProjectFinder.DetermineProjects() with the server-level --autoplan-file-list flag. Changed files are filtered using filterToFileList() and then each file's directory is walked upward to find a Terraform root (presence of *.tf files). Module directories are handled separately via ModuleProjects.DependentProjects() .
With atlantis.yaml#
DetermineProjectsViaConfig() iterates over every project and tests each modified file against the project's autoplan.when_modified patterns using moby/patternmatcher. Key behaviors:
- Patterns are relative to the project directory — Atlantis prepends the project dir before matching against repo-root-relative paths .
- Exclusion patterns prefixed with
!are supported . - A project matches if any modified file matches any pattern .
- If the project directory does not exist on disk, the project is skipped .
Default Patterns#
Defined in raw/autoplan.go and expanded to recursive **/ globs:
| Indicator | Expanded when_modified default |
|---|---|
*.tf* | **/*.tf* |
*.tofu | **/*.tofu |
*.tofu.json | **/*.tofu.json |
terragrunt.hcl | **/terragrunt.hcl |
.terraform.lock.hcl | **/.terraform.lock.hcl |
Always-Ignored Files#
Regardless of patterns, the following files never trigger a plan :
terraform.tfstateterraform.tfstate.backuptflint.hcl
atlantis.yaml Configuration Reference#
projects:
- dir: infra/vpc
autoplan:
enabled: true # default: true
when_modified:
- "*.tf"
- "*.tfvars"
- "!override.tf" # exclusion
The raw config struct is defined in server/core/config/raw/autoplan.go. Both fields are optional — enabled defaults to true and when_modified defaults to the patterns above .
Key Source Files#
| File | Purpose |
|---|---|
server/core/config/raw/autoplan.go | Autoplan struct, defaults, ToValid() |
server/events/project_finder.go | File pattern matching (DetermineProjects, DetermineProjectsViaConfig) |
server/events/command_runner.go | RunAutoplanCommand() — label suppression, global disable |
server/controllers/events/events_controller.go | Webhook routing to handlePullRequestEvent() |
server/events/plan_command_runner.go | runAutoplan() — project command building and execution |