# Manage Multiple Terraform/OpenTofu Versions

Learn how to install and manage multiple versions of Terraform and OpenTofu using the Atmos toolchain manager.

## Overview

Atmos includes a built-in toolchain manager that can install and manage multiple versions of Terraform, OpenTofu, and other infrastructure tools. This is useful when:

- Different projects require different Terraform versions
- You need to test compatibility with newer versions
- Your team needs consistent tool versions across machines

## Installing Tool Versions

### Install a Specific Version

```bash
# Install a specific Terraform version
atmos toolchain install terraform@1.9.0

# Install a specific OpenTofu version
atmos toolchain install opentofu@1.8.0
```

### Install the Latest Version

```bash
# Install the latest Terraform
atmos toolchain install terraform@latest

# Install the latest OpenTofu
atmos toolchain install opentofu@latest
```

## Using .tool-versions

The `.tool-versions` file (compatible with asdf) allows you to pin tool versions per project:

```bash title=".tool-versions"
terraform 1.9.0
opentofu 1.8.0
```

When you run `atmos terraform plan`, Atmos uses `.tool-versions` as the project's default tool environment. With the default [`toolchain.install: auto`](/cli/configuration/toolchain#install) policy, if the file pins the component's executable (`terraform`, or `tofu` when the component sets `command: tofu`), Atmos installs that version when it is missing. Other tools in the file are added to `PATH` only if they are already installed, for example with `atmos toolchain install`. If a tool lists multiple versions, the first is its default. Terraform output lookups, including `!terraform.output` and `atmos.Component()`, use these defaults too.

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. Explicit `dependencies.tools` override the baseline; installation policy controls downloads separately. Under `auto`:

- Helmfile components also install `helm` when the file pins it.
- Custom commands and hooks use only the tools that are already installed. Atmos installs just their own `dependencies.tools`.
- Workflows install every tool listed in `.tool-versions` that is missing before running the first step, because a workflow can call any project tool.

Set `toolchain.install` to change how eagerly Atmos installs tools:

```yaml title="atmos.yaml"
toolchain:
  install: auto   # never | declared | auto | always
```

- `never` installs nothing. Tools are used only when already installed, and a missing `dependencies.tools` entry is an error.
- `declared` installs only `dependencies.tools` (and, for workflows, every `.tool-versions` tool). Other runs inherit installed project selections without installing missing defaults. Projects pinned to an [edition](/cli/configuration/edition) before 2026-10-09 get this value automatically.
- `auto` is the default described above.
- `always` installs every tool in `.tool-versions` for every run.

If the file does not pin the component's executable, Atmos installs nothing for it and uses the system `PATH`. Entries with the version `system`, `ref:...`, or `path:...`, a bare operator such as `~>` (write `~>1.9.0` without a space), or a tool name Atmos cannot resolve are skipped without failing the run, as are lines that list a tool without a version (with a warning). Two entries for the same tool with different versions, such as `tofu 1.12.6` and `opentofu/opentofu 1.11.0`, are an error.

Explicit `dependencies.tools` entries always override `.tool-versions`, even when they select a different version or use a version constraint. For example:

```text title=".tool-versions"
hashicorp/terraform 1.9.0
```

```yaml title="stacks/deploy/prod.yaml"
components:
  terraform:
    legacy-vpc:
      dependencies:
        tools:
          terraform: "1.8.5"
```

Here, `legacy-vpc` uses Terraform `1.8.5`; components without an override use `1.9.0`. Atmos treats a short name or configured alias and its qualified name as the same tool when it compares the manifest with explicit dependencies, so `terraform` overrides `hashicorp/terraform`. Running the component does not change the project pin in `.tool-versions`.

Precedence is `.tool-versions` → stack-wide `dependencies.tools` → component-type dependencies → component-instance dependencies. Existing constraint checks between explicit stack scopes still apply. If neither the manifest nor stack configuration declares a tool, Atmos falls back to the system `PATH`.

### Setting Versions

```bash
# Set Terraform version for current project
atmos toolchain set terraform@1.9.0

# This creates or updates .tool-versions
```

## Listing Installed Versions

```bash
# List all installed tools
atmos toolchain list

# Search for available Terraform versions
atmos toolchain registry search terraform
```

## Component-Level Versions in Stacks

Different Terraform components can use different versions. This is useful when:

- Legacy components require older Terraform versions
- New components need features from newer versions
- Gradual upgrades without breaking existing infrastructure

### Global Tool Dependencies

Define default versions at the root level of your stack:

```yaml title="stacks/orgs/acme/_defaults.yaml"
# Global tool dependencies - applies to all components
dependencies:
  tools:
    terraform: "1.9.8"
    kubectl: "1.28.0"
```

### Component-Type Dependencies

Override for all Terraform components:

```yaml title="stacks/orgs/acme/plat/prod/_defaults.yaml"
terraform:
  dependencies:
    tools:
      terraform: "1.9.8"
      tflint: "0.44.1"
```

### Per-Component Override

Specify versions for individual components:

```yaml title="stacks/orgs/acme/plat/prod/us-east-1.yaml"
components:
  terraform:
    vpc:
      dependencies:
        tools:
          terraform: "1.9.8"   # Use current version
          aws-cli: "2.13.0"

    legacy-app:
      dependencies:
        tools:
          terraform: "1.5.0"   # Legacy component needs older version
          aws-cli: "2.10.0"
```

### How It Works

When you run a component, Atmos automatically:

1. Resolves the tool version from the most specific level
2. Installs the tool if not already cached
3. Executes the command with that version

```bash
# Uses terraform 1.9.8 (from vpc component config)
atmos terraform plan vpc -s prod/us-east-1

# Uses terraform 1.5.0 (from legacy-app component config)
atmos terraform plan legacy-app -s prod/us-east-1
```

For complete documentation on component dependencies, see [Configure Dependencies](/stacks/dependencies).

## Best Practices

1. **Pin exact versions in CI/CD**: Use exact versions (e.g., `1.9.0`) in pipelines for reproducibility
2. **Use constraints in development**: Allow minor version flexibility (e.g., `>=1.9.0, <1.10.0`) for local development
3. **Commit .tool-versions**: Include `.tool-versions` in version control so the team uses consistent versions
4. **Test upgrades in isolation**: Install new versions and test before updating `.tool-versions`

## See Also

- [Configure Dependencies](/stacks/dependencies) - Component-level tool version configuration
- [Toolchain Commands](/cli/commands/toolchain/usage)
- [Toolchain Configuration](/cli/configuration/toolchain/)
