Terraform Configuration
Configure how Atmos executes Terraform and OpenTofu commands, including the base path for components, auto-approve behavior, backend file generation, and initialization options.
The term "Terraform" is used in this documentation to refer to generic concepts such as providers, modules, stacks, the HCL-based domain-specific language and its interpreter. Atmos works with both Terraform and OpenTofu.
Configuration
Configuration Reference
commandSpecifies the executable to run for Terraform commands. Defaults to
terraform.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_COMMANDCommand-line flag:--terraform-commandExamples:
terraform- Use Terraform from PATHtofu- Use OpenTofu from PATH/usr/local/bin/terraform-1.8- Use specific version/usr/local/bin/tofu-1.7.1- Use specific OpenTofu version
base_pathDirectory containing Terraform component directories. Supports absolute and relative paths.
Environment variable:
ATMOS_COMPONENTS_TERRAFORM_BASE_PATHCommand-line flag:--terraform-dirapply_auto_approveWhen
true, Atmos adds-auto-approvetoterraform applycommands, skipping the confirmation prompt.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVEDefault:falsedeploy_run_initWhen
true, Atmos runsterraform initbefore executingatmos terraform deploy.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_DEPLOY_RUN_INITCommand-line flag:--deploy-run-initDefault:trueinit_run_reconfigure(deprecated)Deprecated in favor of
init.reconfigure(see Configuration Reference below). Wheninit.reconfigureis not explicitly set (at any precedence level), Atmos maps this legacy boolean:falsebehaves asinit.reconfigure: never, and the defaulttruebehaves asinit.reconfigure: auto— notalways. This is the one behavior change existing projects relying on the old default will see: Atmos no longer adds-reconfigureto every init by default. Setinit.reconfigure: alwaysexplicitly to keep the previous unconditional behavior.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_INIT_RUN_RECONFIGURECommand-line flag:--init-run-reconfigureDefault:trueauto_generate_backend_fileWhen
true, Atmos generatesbackend.tf.jsonfrom stack configuration before running Terraform commands.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_AUTO_GENERATE_BACKEND_FILECommand-line flag:--auto-generate-backend-fileDefault:trueauto_generate_filesWhen
true, Atmos generates files from thegeneratesection in stack configuration before running Terraform commands.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_AUTO_GENERATE_FILESDefault:falseinit.pass_varsWhen
true, Atmos passes the generated varfile toterraform initusing--var-file. This is primarily useful with OpenTofu, which supports passing varfiles to init for dynamic backend configuration.This also applies to the implicit
initthat Atmos runs while resolving a component referenced through!terraform.outputoratmos.Component— its vars are forwarded asTF_VAR_*environment variables so that modules with init-time variable dependencies (for example, a moduleversionbound to avar) can initialize.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_INIT_PASS_VARSCommand-line flag:--init-pass-varsDefault:falseinit.mode,init.reconfigure,init.upgradeControl whether and how Atmos runs
terraform initautomatically before other commands, beforedeploy(whendeploy_run_initis enabled), and before resolving!terraform.outputoratmos.Component. All three acceptauto,always, ornever, and all three default toautofor unpinned projects and projects pinned to a config edition on or after September 12, 2026; pinning an earlier edition restoresalwaysforinit.modeandneverforinit.upgrade(Atmos's pre-this-setting behavior for each) with no config changes.init.mode—autoskipsterraform initwhen nothing that affects it (root.tf/.tofufiles,.terraform.lock.hcl, the CLI configuration, the resolved binary, relevant environment variables, and — wheninit.pass_varsistrue— the varfile) has changed since the last successful init, and the working directory still looks initialized.alwaysrestores the previous unconditional behavior.neverdisables the ordinary implicit init — but unlike--skip-init, it does not suppress the always-forced reconfigure init thatatmos terraform workspaceselect/newneeds;--skip-initsuppresses init unconditionally, including forworkspace.init.reconfigure—autoadds-reconfigureonly when the backend configuration changed since the last init, on the first init, after a JIT working directory is re-provisioned, or foratmos terraform workspace.alwaysadds it to every init.nevernever adds it.init.upgrade—auto(default) adds-upgradeonly when Terraform or OpenTofu reports that an upgrade is required (for example, a provider version constraint was raised beyond the locked version).nevernever adds it — set this to opt out of automatic upgrades.alwaysadds it to every init.
If a skipped init turns out to have been wrong — a nested module changed, or
.terraformwas altered by hand — Terraform/OpenTofu fails with a diagnostic (e.g. "Backend initialization required", "Required plugins are not installed") before touching state. Atmos recognizes these diagnostics, runs the init it should have run (adding-reconfigure/-upgradewhen the diagnostic calls for it and the setting isn'tnever), and retries the command once. No retry happens when init was explicitly disabled (--skip-init,init.mode: never,deploy_run_init: false). Atmos never runs-migrate-stateas part of this recovery. See Automatic Initialization for the full behavior.An explicit
atmos terraform initinvocation always runs, regardless of these settings, and refreshes the cached fingerprint. Deleting.terraform(including withatmos terraform clean) forces a fresh init on the next command.Environment variables:
ATMOS_COMPONENTS_TERRAFORM_INIT_MODE,ATMOS_COMPONENTS_TERRAFORM_INIT_RECONFIGURE,ATMOS_COMPONENTS_TERRAFORM_INIT_UPGRADECommand-line flags:--init-mode,--init-reconfigure,--init-upgradeDefault:autofor all threeplan.skip_planfileWhen
true, Atmos skips creating a plan file duringterraform plan. The plan output is shown but not saved.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_PLAN_SKIP_PLANFILECommand-line flag:--skip-planfileDefault:falseplanfilesConfigures planfile storage and drift verification for CI pipelines. Storage is opt-in: when this section is absent, planfile upload, download, and verification are skipped (and an explicit
--verify-planrequest errors, since there is no stored plan to verify against).components:terraform:planfiles:stores:github:type: github/artifactspriority: [github]verify: failplanfiles.stores- Named store definitions. Each store has a
type(github/artifacts,aws/s3, orlocal/dir) and backend-specificoptions. planfiles.default- Name of the store to use when no priority list is set.
planfiles.priority- Ordered list of store names to try in turn for upload and download.
planfiles.verifyDrift-verification behavior on
deploy:fail(default under CI),warn, oroff. Precedence is CLI flag (--verify-plan/--verify-plan=false) > config > CI default.Environment variable (per-run override):
ATMOS_TERRAFORM_VERIFY_PLANplanfiles.required- Whether a stored planfile must exist for
deployto proceed. Unset, it tracksverifystrictness (required when verification resolves tofail).
See Planfile Storage for backend configuration and Terraform Planfiles for verification semantics.
plugin_cacheWhen
true, Atmos enables Terraform provider plugin caching by automatically settingTF_PLUGIN_CACHE_DIR. This improves performance by reusing downloaded providers across components, reducing init times and network bandwidth.This is Terraform's own provider cache and is independent of the registry cache (
cache). The plugin cache stores provider plugins that Terraform manages itself; the registry cache is an Atmos-managed proxy that caches both provider and module registry traffic. The two are separate layers and can be used together.When caching is enabled, Atmos also sets
TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE=trueas required by Terraform.If
TF_PLUGIN_CACHE_DIRis already set in your environment or via the globalenv:section inatmos.yaml, Atmos does not override it—you manage your own cache.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_PLUGIN_CACHEDefault:trueplugin_cache_dirCustom directory path for the Terraform plugin cache. If empty (default), Atmos uses the XDG cache directory:
~/.cache/atmos/terraform/plugins.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_PLUGIN_CACHE_DIRDefault:""(uses XDG cache directory)ui.enabledWhen
true, Atmos displays a streaming terminal UI duringterraform plan,apply,deploy,init, anddestroycommands. The UI shows real-time resource status with progress indicators, similar to Docker build output.refreshdoesn't support the streaming UI (Terraform'srefreshdoesn't emit the structured-jsonoutput it depends on); passing--uiwithrefreshprints a warning and falls back to standard output.The streaming UI automatically disables when:
- Output is piped or redirected (no TTY)
- Running in CI environments (
CI=true) - Using commands that don't support JSON streaming (
refresh)
--uiis rejected with an error (not a silent fallback) when combined with--max-concurrencygreater than1: concurrently-scheduled components would each try to take over the same terminal with their own full-screen UI session. Use--max-concurrency 1(the default) with--ui, or drop--uito run components concurrently.Environment variable:
ATMOS_TERRAFORM_UICommand-line flag:--uiDefault:falseExample output during apply:
⠋ apply plat-ue2-dev/vpc━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 40% (2/5) 12.3s✓ Created aws_vpc.main (2.1s)✓ Created aws_subnet.public[0] (1.3s)⠋ Creating aws_security_group.default (5.2s)○ Pending aws_instance.web[0]On completion, output condenses to a summary:
✓ Apply plat-ue2-dev/vpc completed (15.2s)auto_provision_workdir_for_outputsWhen
true(default), Atmos automatically provisions the JIT working directory for components withprovision.workdir.enabled: truebefore runningterraform initduring!terraform.outputoratmos.Componentevaluation. This enables cross-component output references to work on any machine, including fresh CI runners where the workdir has not been created yet.Set to
falseto disable auto-provisioning.terraform initwill still run, but only if the workdir has already been created by a prior deploy; on a fresh machine with no prior workdir it will fail.For state-only reads, prefer
!terraform.state— it reads the state file directly with noterraform init, no workdir provisioning step, and no terraform binary required.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_AUTO_PROVISION_WORKDIR_FOR_OUTPUTSDefault:trueshell.promptCustom prompt to display when running
atmos terraform shell.
Using OpenTofu
To use OpenTofu instead of Terraform, set the command to tofu:
OpenTofu supports additional features like passing variables to init for dynamic backend configuration:
CLI Configuration (rc)
The rc section declares the Terraform/OpenTofu CLI configuration (.terraformrc / .tofurc). Atmos renders it and exposes it to the subprocess via TF_CLI_CONFIG_FILE and TOFU_CLI_CONFIG_FILE — no hand-managed dotfiles. It is a near-opaque passthrough: keys map directly to CLI-config directives, so new directives work without an Atmos release.
enabled- Enable rendering of the CLI configuration. Defaults to
false. - (remaining keys)
- Rendered verbatim into Terraform's native CLI config (HCL):
provider_installation,host,credentials,plugin_cache_dir, etc.
components:
terraform:
rc:
enabled: true
provider_installation:
- network_mirror:
url: "https://terraform-mirror.example.com/"
- direct:
exclude:
- "registry.terraform.io/hashicorp/*"
If you already manage your own CLI config (via TF_CLI_CONFIG_FILE, TOFU_CLI_CONFIG_FILE, or the legacy TERRAFORM_CONFIG), Atmos detects it and defers to you.
Registry Cache (cache)
The cache section enables the Terraform registry cache — transparent provider and module caching via an ephemeral local network-mirror proxy. It is execution-environment (runner) configuration that merges through the stack hierarchy.
This is not the same as Terraform's plugin_cache: the registry cache is an Atmos-managed proxy that caches both provider and module registry traffic (default root ~/.cache/atmos/terraform/registry), whereas plugin_cache is Terraform's own provider plugin cache (~/.cache/atmos/terraform/plugins). They are independent layers.
enabledEnable the registry cache. Defaults to
Environment variable:false.ATMOS_COMPONENTS_TERRAFORM_CACHE_ENABLEDlocationCache root. Defaults to the XDG cache directory under
Environment variable:terraform/registry.ATMOS_COMPONENTS_TERRAFORM_CACHE_LOCATIONbackend.type- Storage backend.
filesystem(default). metadata_ttl- Time-to-live for registry metadata (Go duration). Defaults to
24h. stale_while_revalidate- Window during which stale metadata may be served while revalidating (Go duration). Defaults to
168h. mirror.enabled- Express fleet pre-seeding policy. The
cache mirrorcommand runs regardless. Defaults tofalse.
Target platforms are configured at the project level via platforms (not under cache), since the same list also drives multi-platform lock completion.
components:
terraform:
platforms:
- linux_amd64
- darwin_arm64
- windows_amd64
cache:
enabled: true
metadata_ttl: 24h
stale_while_revalidate: 168h
Manage the cache with atmos terraform cache list|stats|prune|delete|mirror.
Platforms (platforms)
components.terraform.platforms is the list of target platforms a project builds for, as <os>_<arch> (e.g. linux_amd64, darwin_arm64). A single list drives two things:
- Eager pre-seeding
- The lazy proxy only caches the platform Terraform requests at run time (your host's). Run
atmos terraform cache mirrorto pre-fetch every platform inplatformsinto the same cache directory — for mixed CI/developer fleets or air-gapped bundles. - Complete lock files
- When a customized provider installation method is active (Terraform's default
plugin_cache, or the registrycache),initwrites a.terraform.lock.hclwith checksums for only the host platform and warns "Incomplete lock file information for providers". Withplatformsdeclared, Atmos runsproviders lockafter init to complete the lock for every platform, so a committed lock installs cleanly across a fleet.
components:
terraform:
platforms:
- linux_amd64
- darwin_arm64
- windows_amd64
For ephemeral or vendored components (provision.workdir or a source:) the canonical .terraform.lock.hcl has no committable home, so Atmos keeps the committed lock per instance as .<stack>-<component>.terraform.lock.hcl — restored into the working directory before init and persisted after. Keep canonical **/.terraform.lock.hcl ignored and un-ignore !**/.*-*.terraform.lock.hcl in .gitignore. Plain in-repo components keep committing the canonical lock as usual.
Flags (flags)
components.terraform.flags sets fleet-wide default values for Terraform CLI execution
flags — most importantly -lock-timeout, which controls how long Terraform retries a
held state lock before failing. Terraform's own default is 0s (fail immediately), which
causes avoidable failures when concurrent pipelines (e.g. a CI matrix, or multiple
engineers) touch the same component's state around the same time. Declaring a
lock_timeout here means every plan/apply/destroy/refresh/import picks it up
automatically, without retyping -lock-timeout=<duration> on every invocation.
components:
terraform:
flags:
lock_timeout: "5m"
parallelism: 10
refresh: true
compact_warnings: true
The same flags: block can also be set at the stack level (a root-level terraform:
block, sibling to components:, applying to every terraform component in that stack or
import) and per component (inside a specific component's definition), overriding the
fleet-wide default field-by-field:
# stack manifest
terraform:
flags:
lock_timeout: "5m" # applies to every terraform component in this stack
components:
terraform:
vpc:
flags:
lock_timeout: "10m" # overrides the stack-wide default just for this component
Precedence (lowest to highest): Terraform's own built-in default < atmos.yaml
components.terraform.flags < an ATMOS_COMPONENTS_TERRAFORM_FLAGS_* environment
variable (overrides the atmos.yaml value) < stack-level terraform.flags < a
component's own flags: block < an explicit CLI-typed flag (e.g.,
atmos terraform plan vpc -s plat-ue2-dev -- -lock-timeout=30s), which always wins.
Not every flag applies to every subcommand — Atmos only injects a flag where Terraform actually supports it:
flags.lock_timeoutDefault value for
-lock-timeout(e.g."30s","5m"). Applies toplan,apply,destroy,refresh, andimport.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_FLAGS_LOCK_TIMEOUTDefault: unset (Terraform's own0sapplies)flags.lockDefault value for
-lock. Applies toplan,apply,destroy,refresh, andimport.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_FLAGS_LOCKDefault: unset (Terraform's owntrueapplies)flags.parallelismDefault value for
-parallelism. Applies toplan,apply,destroy, andrefresh. Not supported byterraform import.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_FLAGS_PARALLELISMDefault: unset (Terraform's own10applies)flags.refreshDefault value for
-refresh. Applies toplan,apply(only when not consuming a pre-built plan — Terraform rejects-refreshonapply <planfile>), anddestroy. Not supported byterraform refreshitself orterraform import.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_FLAGS_REFRESHDefault: unset (Terraform's owntrueapplies)flags.compact_warningsDefault value for
-compact-warnings. Applies toplan,apply,destroy, andrefresh. Not supported byterraform import.Environment variable:
ATMOS_COMPONENTS_TERRAFORM_FLAGS_COMPACT_WARNINGSDefault:false
For a one-off override without changing configuration, pass the raw Terraform flag directly on the command line — it always wins over every layer above:
atmos terraform plan vpc -s plat-ue2-dev -- -lock-timeout=30s
Generated Files
When auto_generate_backend_file is enabled, Atmos generates a backend.tf.json file in each component directory. Add this to your .gitignore:
# Atmos generated files
backend.tf.json
Related Commands
Execute Terraform/OpenTofu commands
Generate and show execution plan
Apply infrastructure changes
Run init, plan, and apply in sequence
Open an interactive shell
Generate backend configuration files
Generate variable files
Generate auxiliary configuration files