Skip to main content

Your .tool-versions Now Picks the Terraform Every Component Runs

· 4 min read
Erik Osterman
Founder @ Cloud Posse

You pin hashicorp/terraform 1.15.8 in .tool-versions, run atmos terraform plan, and Atmos quietly runs whatever Terraform happens to be first on your PATH — a Homebrew upgrade, a teammate's tfenv shim, or an older binary on a CI runner. The Terraform versions guide promised the pinned version; components didn't deliver it. Now every command and component uses .tool-versions as its baseline.

The Problem​

Atmos already reads .tool-versions for atmos toolchain commands and workflows, but component runs only looked at explicit dependencies.tools in stack configuration. A project that pinned its tools in one file still got whatever executable the machine offered when it ran atmos terraform plan, atmos helmfile diff, or a !terraform.output lookup. Custom commands and hooks ignored the file too.

Fixing that naively would create new problems. Many .tool-versions files are shared with asdf or mise and list tools Atmos doesn't manage (nodejs, python) or versions like system. And nobody wants atmos describe stacks to download every tool in the file just to evaluate a template.

The Fix​

The project's .tool-versions is now a baseline for every command and component type, with installs limited to what a run actually executes:

  • Components install their own executable when the file pins it — terraform, tofu when the component sets command: tofu, helmfile (plus helm), packer, or ansible. Other tools in the file are added to PATH only if they're already installed. Nothing else is downloaded during a component run, including read-only commands like describe, plan --dry-run, and terraform version.
  • Every Atmos command inherits already-installed versions selected by the file on PATH. Cached tools absent from the file, and other cached versions, are not added.
  • Custom commands and hooks install only their own dependencies.tools.
  • Workflows keep their existing behavior: they install every listed tool that is missing before the first step.

How much Atmos installs on its own is a setting, so you can make it more or less eager (see Control What Atmos Installs).

Explicit dependencies.tools always win, even when one side uses a short name or alias and the other uses owner/repo. Entries Atmos can't use — system, ref: and path: versions, unknown tools, lines without a version, or ~> 1.15.0 written with a space — are skipped instead of failing the run. Two entries for the same tool with different versions (tofu 1.12.6 and opentofu/opentofu 1.11.0) now stop with an error naming both, instead of picking one at random.

How to Use It​

Pin the tool once:

.tool-versions
hashicorp/terraform 1.15.8

Every component without an override runs Terraform 1.15.8, installing it on first use. Override a single component when it needs something else:

stacks/deploy/prod.yaml
components:
terraform:
legacy-vpc:
dependencies:
tools:
terraform: "1.15.6"

Running legacy-vpc uses 1.15.6 and leaves .tool-versions untouched. If your configuration points at a different manifest, toolchain.file_path is now honored everywhere.

If a component previously relied on a binary from PATH while .tool-versions pinned a different version, it now runs the pinned version. Remove or update the pin, or set dependencies.tools on the component, to keep the old binary.

Control What Atmos Installs​

The new toolchain.install setting controls downloads separately from version selection. Every command inherits installed project selections under every policy; explicit dependencies.tools override them. The setting decides which missing tools Atmos installs on its own:

atmos.yaml
toolchain:
install: auto # never | declared | auto | always
  • never installs nothing. Tools are used only when they are already installed, and a missing dependencies.tools entry fails with a hint to run atmos toolchain install. Use it on air-gapped runners or when an image bakes in every tool.
  • declared installs only explicit dependencies.tools (and, for workflows, every .tool-versions tool). Other runs use already-installed project selections without installing missing defaults.
  • auto is the default and the behavior described above.
  • always installs every tool in .tool-versions for every run, including custom commands and hooks.

Projects pinned to an edition dated before 2026-10-09 keep declared automatically, preserving their automatic download policy. Installed project selections still supply the baseline under every policy. Set ATMOS_TOOLCHAIN_INSTALL to override the setting for a single run.

Get Involved​

Questions or edge cases with your .tool-versions? Open an issue or join the conversation in the Cloud Posse community.