linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Knowledge base
  • Cheat sheet
  • Capstone
  • Interview prep
home/terraform/kb/Terraform basics/tf-version-constraints

kb/core ── Terraform basics ── beginner

Version constraints in Terraform: required_version and providers

required_version pins which versions of terraform may run this code. required_providers.version does the same for providers. The pessimistic operator ~> 5.60 is the standard: it allows minor updates and blocks major ones.

view as markdownaka: version-constraints, terraform-version

Why pin versions

Without explicit constraints, terraform downloads "the latest," which means that six months from now a colleague might be on a different major version and get a different plan from the same code. That breaks determinism, the main point of IaC.

So in HCL you write two constraints:

hcl
terraform {
  required_version = ">= 1.5, < 2.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.60"
    }
  }
}
  • required_version is which versions of the terraform tool itself may run this code.
  • required_providers.<name>.version is which versions of the provider.

Both constraints are checked when any command starts (init, plan, apply). If something does not match, terraform refuses to run and prints a clear message.

Operators

FormWhat it meansWhen to use it
1.9.5 or = 1.9.5exact matchrarely, only when a specific version has a bug
>= 1.5no lower thane.g. for required_version, open upper bound
<= 5.60no higher thanrarely
~> 5.605.60.x, 5.61.x, ..., but not 6.xthe standard for providers
~> 5.60.0only 5.60.xvery strict, usually overkill
>= 1.5, < 2.0compoundthe standard for required_version
!= 5.55.0excluderarely, for a known bad version

Several constraints joined by a comma act as a logical AND.

The pessimistic constraint ~>

This is the most common operator. ~> X.Y means "X.Y or newer, but no higher than X+1.0."

Concretely:

  • ~> 5.60 becomes >= 5.60, < 6.0. 5.60.x, 5.61.x, and 5.99.999 all fit; 6.0.0 does not.
  • ~> 5.60.0 becomes >= 5.60.0, < 5.61.0. Only patches inside 5.60 fit.

For providers the standard is ~> X.Y. It allows bug fixes and new resources while protecting you from major releases with breaking changes.

For required_version the standard is >= X.Y, < X+1. The terraform tool follows SemVer more strictly than providers do, so you set the upper bound at the next major.

required_version: what it controls

  • It blocks a run on a version that is too old (it lacks the features your HCL relies on).
  • It blocks a run on a version that is too new (when you set an upper bound).

Good practice is to set >= X.Y, < X+1.0, where X.Y is the version the code was actually written against. Avoid an open upper bound (>= 1.5 with no ceiling): that lets terraform 2.x into the project with potentially breaking changes.

Where version constants live between runs

  • The required_* blocks live in .tf files; you write them by hand.
  • The exact installed versions live in the [[tf-lockfile|lockfile]] (.terraform.lock.hcl).
  • The provider binary cache lives in .terraform/providers/.

The lockfile pins the exact version within the constraint. The constraint defines the range terraform searches for a new version on init -upgrade.

How to move to a new major version

Say the code has aws ~> 5.60 and the AWS provider has released 6.0:

  1. Read the changelog between 5.x and 6.0. It lists the breaking changes.
  2. Adjust the HCL for the new attribute and resource names.
  3. Change the code to version = "~> 6.0".
  4. Run terraform init -upgrade; the lockfile updates.
  5. Run terraform plan and check that the diff matches what you expect (only the planned changes, no surprises).
  6. Only then run apply.

Never do step 4 without step 1.

Pitfalls

  • An open upper bound is a landmine. >= 5.0 with no ceiling will let a major version through automatically. Six months later this turns into an urgent hotfix on a Friday.
  • ~> X.Y with two numeric segments is not the same as ~> X.Y.Z. It is easy to mix up. Remember a simple rule: the dot to the right of ~> is "freedom inside," the left is "upper bound."
  • required_version is sometimes forgotten. Without it the code runs on any terraform, including a badly outdated one. At a minimum, write required_version = ">= 1.5".
  • required_providers without version is also valid. It means "any version." Do not do this; it throws away determinism right away.
  • required_providers goes inside the terraform {} block. The version field inside provider "..." {} is deprecated. It shows up often in old code; move it up. See tf-provider-block.

§ команды

bash
terraform version

The current terraform version and installed plugins. Compare it with required_version in the HCL.

bash
terraform init -upgrade

Bump providers up to the newest versions allowed by the current constraint.

bash
terraform providers

The tree of providers and their versions, including transitive ones.

§ см. также

  • tf-initterraform init: the first command in any projectterraform init downloads the provider plugins (AWS, GCP, and so on), creates a lockfile that pins their versions, and prepares the working directory. Without it, neither plan nor apply will run.
  • tf-lockfile.terraform.lock.hcl: pinning provider versionsThe lockfile pins the exact provider versions and their hashes, so you and CI always run the same build. It is created on terraform init and updated through init -upgrade. Commit it to git.
  • tf-provider-blockThe provider block: who Terraform will callThe provider block configures the plugin: which AWS region to talk to, which endpoints to use, which credentials to take. One block per provider is usually enough.
  • hcl-syntaxHCL: the language you write Terraform inHCL (HashiCorp Configuration Language) is the language you use to describe the desired state of your infrastructure. It looks like JSON, but it is easier to read: you can write comments, variables, and loops.
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies