Skip to main content

Stop retyping -lock-timeout on every terraform command

· 3 min read
Erik Osterman
Founder @ Cloud Posse

Terraform locks state before it writes to it, and by default it gives up the instant that lock is already held — no retry, no wait. That's fine for a single engineer running commands one at a time. It falls apart the moment two pipelines, or a pipeline and an engineer, touch the same component's state around the same moment: whichever process loses the race just fails, even though the lock would have cleared in a few seconds.

The Problem

Terraform and OpenTofu support -lock-timeout=<duration> to poll for a held lock instead of failing immediately, but it has to be typed on every single invocation. TF_CLI_ARGS_<command> can automate it, but it is a single flat string rather than a structured, independently merged flags: block — so a component that wants to override one flag has to restate the rest or lose them. Teams running concurrent CI matrices, or Atmos Pro-driven deployments, would hit avoidable failures from Terraform's 0s default when no lock timeout is configured.

The Fix

Terraform CLI execution flags — lock_timeout, lock, parallelism, refresh, and compact_warnings — can now be declared once under a flags: block: globally in atmos.yaml, for an entire stack, or for a single component, with each layer overriding the one before it. An ATMOS_COMPONENTS_TERRAFORM_FLAGS_* environment variable can override the atmos.yaml default too — the same environment-variable handling every other Atmos setting already gets, not a special case invented for these five flags. An explicit flag typed directly on the command line still wins over every declared default, so nothing about existing one-off usage changes.

Atmos only injects a flag into commands that actually support it — for example -refresh isn't valid when applying a saved plan, and terraform import doesn't accept -parallelism — so you don't have to track those exceptions yourself.

How to Use It

# atmos.yaml — fleet-wide default
components:
terraform:
flags:
lock_timeout: "5m"
parallelism: 10

# stack manifest — applies to every terraform component in this stack
terraform:
flags:
lock_timeout: "5m"

components:
terraform:
vpc:
# per-component override
flags:
lock_timeout: "10m"
# one-off override still works exactly as before, and always wins
atmos terraform plan vpc -s plat-ue2-dev -- -lock-timeout=30s

# environment variable overrides the atmos.yaml default — handy for a per-CI-job
# tweak without touching the fleet-wide config
ATMOS_COMPONENTS_TERRAFORM_FLAGS_LOCK_TIMEOUT=2m atmos terraform plan vpc -s plat-ue2-dev

Why not just use TF_CLI_ARGS?

Yes, you could. Atmos's env: section also merges by stack and component. Set env: { TF_CLI_ARGS_plan: "-lock-timeout=5m" }, and it scopes the same way flags: does.

But TF_CLI_ARGS_plan is one flat string with no separate fields. If a component overrides -parallelism, it must also restate -lock-timeout, or the component loses that value.

Fields in flags: merge one at a time. A component can override parallelism alone and still inherit lock_timeout from the stack. Every other Atmos section merges the same way.

Get Involved

See the Terraform Configuration docs for the full field reference, defaults, and environment variable overrides. Have feedback on this feature? Open an issue or join the conversation in the Cloud Posse community Slack.