terraform init
terraform init is the first command you run in any new or freshly cloned Terraform configuration. It prepares the working directory by downloading the providers your code references, installing any modules, and configuring the backend that stores your state. Until you run it, no other command (plan, apply, validate) will work, because Terraform has no provider plugins to talk to and no idea where state lives. It is safe to run repeatedly and is idempotent — running it on an already-initialized directory simply confirms everything is in place.
What init actually does
A single init performs several distinct steps in order. Understanding them makes the output far easier to read and the failures far easier to debug.
| Step | What happens | Where it lands |
|---|---|---|
| Backend init | Reads the backend block, prompts for credentials, configures remote/local state | .terraform/terraform.tfstate (backend config cache) |
| Module install | Downloads module sources (registry, Git, local paths) | .terraform/modules/ |
| Provider install | Resolves version constraints and downloads provider plugins | .terraform/providers/ |
| Lock file | Records exact provider versions and checksums | .terraform.lock.hcl |
Consider a minimal AWS configuration:
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40"
}
}
backend "s3" {
bucket = "acme-tfstate-prod"
key = "network/terraform.tfstate"
region = "us-east-1"
use_lockfile = true
}
}
provider "aws" {
region = "us-east-1"
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "main-vpc"
}
}
Running init in this directory produces:
terraform init
Output:
Initializing the backend...
Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.40"...
- Installing hashicorp/aws v5.42.0...
- Installed hashicorp/aws v5.42.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
OpenTofu users run the identical command as
tofu init. The behavior, flags, and.terraform.lock.hclformat are compatible, so the rest of this page applies to both tools.
The .terraform directory and lock file
After init, two artifacts appear:
.terraform/— a local cache containing downloaded providers, installed modules, and the resolved backend configuration. This directory is machine-specific and disposable. Add it to.gitignore; never commit it..terraform.lock.hcl— the dependency lock file. It pins the exact provider versions and their checksums (including per-platform hashes) so every teammate and your CI pipeline get bit-for-bit identical plugins. Commit this file.
A lock file entry looks like this:
provider "registry.terraform.io/hashicorp/aws" {
version = "5.42.0"
constraints = "~> 5.40"
hashes = [
"h1:abc123...",
"zh:0a1b2c...",
]
}
When to re-run init
You re-run init whenever the things it manages change. The flags below cover the common cases.
| Flag | Use it when |
|---|---|
-upgrade | You want the newest provider/module versions allowed by your constraints, refreshing the lock file |
-reconfigure | You changed backend config and want to discard the cached settings, starting fresh |
-migrate-state | You changed backends (e.g. local to S3) and want to copy existing state to the new one |
-backend-config=... | You supply backend settings from a file or key=value pair at init time |
-get=false | You want to skip module installation |
Upgrading providers and modules
The lock file deliberately keeps versions stable. To pick up newer versions within your version constraints, run:
terraform init -upgrade
Output:
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.40"...
- Installing hashicorp/aws v5.49.0...
- Installed hashicorp/aws v5.49.0 (signed by HashiCorp)
Terraform has made some changes to the provider dependency selections recorded
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.
Reconfiguring or migrating the backend
If you edit the backend block, the next plain init detects the mismatch and stops, asking you to choose. Use -reconfigure to ignore the old cached state association and adopt the new config as-is, or -migrate-state to copy your existing state into the new backend:
# Adopt new backend settings, ignore the cached ones
terraform init -reconfigure
# Move existing state into a newly configured backend
terraform init -migrate-state
Output:
Initializing the backend...
Terraform detected that the backend type changed from "local" to "s3".
Do you want to copy existing state to the new backend?
Pre-existing state was found while migrating the previous "local" backend
to the newly configured "s3" backend. Enter "yes" to copy and "no" to
start with an empty state.
Enter a value: yes
Successfully configured the backend "s3"!
Always back up your state file before running
-migrate-state. State migration is one of the few init operations that can lose data if a credential or network failure interrupts it mid-copy.
Best practices
- Commit
.terraform.lock.hcland ignore.terraform/— this is the single most important convention for reproducible builds. - Run
terraform initearly in every CI pipeline beforevalidateandplan; add-input=falseso it fails fast instead of hanging on prompts. - Use
init -upgradedeliberately on its own commit so version bumps are reviewable, never silently mixed with logic changes. - Pin provider versions with pessimistic constraints (
~> 5.40) rather than leaving them open, soinitis predictable. - Keep backend settings out of code with
-backend-config=prod.tfbackendfiles when you reuse one configuration across environments. - After cloning a repo, run
initbefore anything else — the absence of.terraform/is expected, not an error.