TerraformV · The Terraform WorkflowInit
terraform init in Depth
What you'll learn
- Explain what `terraform init` does and what it does not do
- Describe the dependency lock file and its role in reproducibility
- Recognise common init failure modes and how to recover
- Apply the right init flags for the right context
Prerequisites
Verified against Terraform CLI 1.9.x · OpenTofu 1.7.x · HCL 2.0 · bpg/proxmox provider 0.66+ · hashicorp/local provider 2.5+ · hashicorp/null provider 3.2+ · hashicorp/random provider 3.6+ · hashicorp/http provider 3.4+ · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · 2026-08-12
terraform init is the step that prepares a working directory for
use. It is the only step that talks to the provider registry or
the module sources. It is the source of subtle production
surprises when the lock-file contract is violated. This lesson
covers what init does, what the dependency lock file is, and how
to recover from common init failures.
What init does in order
terraform init
↓
1. Read configuration
- Look for `terraform { required_providers, backend }` blocks
↓
2. Resolve provider sources
- registry.terraform.io
- Internal mirrors
- Local filesystem paths
↓
3. Download providers
- Hashicorp-validated providers from the registry
- Checksum-verified against the lock file
↓
4. Resolve module sources
- registry, git, github, hg, s3, gcs, local
↓
5. Download modules
↓
6. Initialise the backend
- Local: create terraform.tfstate
- Remote: negotiate with the backend (S3, GCS, Consul, etc.)
↓
7. Write or update .terraform.lock.hcl
↓
8. Create the .terraform/ working directory
- provider plugins
- module downloads
- backend configuration
Each step is conditional. A terraform init on a simple
configuration with no providers and no modules may download
nothing and create a minimal .terraform/ directory.
The dependency lock file
The .terraform.lock.hcl file is the reproducibility contract:
# This file is maintained automatically by "terraform init".
# Manual edits may be lost - proceed with caution!
provider "registry.terraform.io/hashicorp/local" {
version = "2.5.1"
hashes = [
"h1:abc123...",
"zh:def456...",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.6.0"
hashes = [
"h1:...",
"zh:...",
]
}
The lock file records:
- The exact version of each provider.
- The hashes of the provider binary, verified against the registry.
The lock file is typically committed to source control. The exception is local-only or gitignored working directories.
The lock file is what defends the installation of the
configuration. Without a lock file, every terraform init may
download a different provider patch version, and the underlying
provider API behaviour may differ.
When init fails
The common init failure modes:
Provider download failure
Error: Failed to install provider
Plugin reinitialization required. Please run "terraform init".
The provider cannot be downloaded. Causes:
- The provider source is unreachable (network, registry outage).
- The provider version is not in the registry.
- The providers checksum does not match the lock file.
Recovery:
# Verify the source is reachable
curl -fsSL https://registry.terraform.io/.well-known/terraform.json
# Bypass the lock file (if you are sure of the new version)
terraform init -upgrade
# Reset the lock file and re-download
rm -rf .terraform .terraform.lock.hcl
terraform init
Backend initialisation failure
Error: Failed to get existing workspaces: S3 bucket does not exist
The backend cannot be initialised. Causes:
- The backend credentials are missing or invalid.
- The backend resource (e.g. S3 bucket) does not exist.
- The backend configuration has changed since the last init.
Recovery:
# Run with verbose logging to see the underlying error
TF_LOG=info terraform init
# Reconfigure the backend (does not re-download providers)
terraform init -reconfigure
# Migrate the backend (see Part XXXV)
terraform init -migrate-state
Module source failure
Error: Failed to download module
The module source cannot be reached. Causes:
- The Git source is unreachable.
- The module version does not exist in the source.
- The module source has been removed.
Recovery:
# Verify the source is reachable
git ls-remote https://github.com/example/terraform-module
# Reinitialise modules (does not re-download providers)
terraform get -update
Lock-file mismatch
Error: Failed to install provider
Lock file does not match the installed providers.
The lock files recorded hashes do not match the local
.terraform/ directory. Causes:
- The lock file was updated, but
terraform initwas not run. - The
.terraform/directory was partially deleted. - The provider binary was modified.
Recovery:
# Reinitialise from the lock file
terraform init -upgrade
# Or reset and re-download
rm -rf .terraform
terraform init
Init flags
A few flags worth knowing:
# Upgrade providers within the constraints
terraform init -upgrade
# Reconfigure the backend, do not re-download providers
terraform init -reconfigure
# Migrate state to a new backend without losing state
terraform init -migrate-state
# Skip provider backend initialisation (rare; useful for offline analysis)
terraform init -backend=false
# Skip provider download (useful for syntax-only checks)
terraform init -provider=false
# Skip module download
terraform init -module=false
The .terraform/ directory
The .terraform/ directory is the working cache. It contains:
providers/registry.terraform.io/.../— the provider binaries.modules/...— the downloaded modules.environment— a small file recording the working directory.
The .terraform/ directory is not committed to source control
(.gitignore excludes it). It is regenerated by terraform init.
The .terraform/ directory should be considered build cache, not
state. It is safe to delete and rebuild. The state is in the
backend.
Provider plugin cache
For teams with many engineers, the provider-plugin-cache-dir CLI
config setting avoids re-downloading providers across workspaces:
# ~/.terraformrc
provider_installation {
methods {
- filesystem_mirror {
path = "/usr/local/share/terraform/plugins"
include = ["registry.terraform.io/*/*"]
}
}
}
When configured, every terraform init checks the local mirror
before reaching the registry. This is a meaningful speedup for
large workspaces and a defence against registry outages.
What init does not do
- Does not validate the configuration.
terraform validatedoes that. - Does not compute a plan.
terraform plandoes that. - Does not apply the configuration.
terraform applydoes that. - Does not touch the state. Init does not change state.
- Does not download providers without a
required_providersblock. The configuration must declare which providers to use.
A production init workflow
# CI: always run init with the lock file
terraform init -input=false
# CI: validate the lock file
terraform providers lock -platform=linux_amd64 -platform=darwin_amd64 -platform=darwin_arm64
# Local: when changing versions
terraform init -upgrade
# Local: when changing backend
terraform init -migrate-state
The terraform providers lock command is the CI gate for the
lock file. It re-records the hashes for the specified platforms
and fails if the recorded hashes do not match the available
binaries.
What comes next
The next lesson is terraform plan in depth. The plan is the
operational artefact; the course has already covered it
holistically, and the next lesson is a deeper dive into the plan
output.
Knowledge check · 7 questions
Q1. What is the role of terraform fmt?
Q2. What is the role of terraform validate?
Q3. What is the role of terraform init?
Q4. terraform plan modifies the real world.
Q5. Which of the following are part of the safe workflow? (Select all that apply.)
Q6. What does terraform show do?
Q7. A team runs terraform plan and sees no changes. The apply also shows no changes. The real world has drifted. What should they do?
Passing score: 75%. Answers are checked in this browser.