.tfvars file format
The format is plain HCL without blocks: just variable_name = value pairs:
# prod.tfvars
env = "prod"
region = "us-east-1"
instance_count = 3
enable_logging = true
tags = {Project = "my-app"
Environment = "prod"
ManagedBy = "terraform"
}
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
No variable {} blocks, only assignments. If a variable with that name is not declared in HCL, apply fails with undeclared variable.
.tfvars.json: the same in JSON
When files are generated by another program (Jenkins, a templating engine), JSON is more convenient:
{"env": "prod",
"region": "us-east-1",
"instance_count": 3,
"tags": {"Project": "my-app",
"Environment": "prod"
}
}
The filename must end in .tfvars.json, not just .json.
What is loaded automatically
Terraform reads these files without any flags, simply because they are in the project root:
terraform.tfvarsterraform.tfvars.json*.auto.tfvars(any name with this suffix)*.auto.tfvars.json
For load order and precedence, see tf-variable-sources.
-var-file for a specific environment
In CI/CD it is common to keep separate files:
envs/
dev.tfvars
staging.tfvars
prod.tfvars
main.tf
Run them like this:
terraform plan -var-file=envs/prod.tfvars
terraform apply -var-file=envs/prod.tfvars
These files are not loaded automatically (no auto suffix). You must pass them via -var-file.
You can pass multiple files:
terraform plan -var-file=envs/base.tfvars -var-file=envs/prod.tfvars
The last file wins on collision. This is a useful pattern: base values are shared, and the environment file overrides them.
Complex types in .tfvars
HCL supports nested complex types:
# databases.tfvars
databases = { primary = {engine = "postgres"
instance_class = "db.t3.medium"
allocated_storage = 100
}
analytics = {engine = "postgres"
instance_class = "db.t3.small"
allocated_storage = 200
}
}
HCL allows trailing commas and line breaks; the format is human-readable.
Pitfalls
-
terraform.tfvars is often in .gitignore. It holds a developer's local values, potentially with secrets. The standard approach:
gitignoreterraform.tfvars
*.auto.tfvars
!envs/*.tfvars
Environment files in
envs/are committed (without secrets);terraform.tfvarsand auto files are ignored. -
All
*.auto.tfvarsfiles are loaded. If the repo containsdev.auto.tfvarsandprod.auto.tfvars, both are read automatically, and the last one alphabetically overrides the first. This is a common trap when working with environments. Use-var-fileif you need explicit control. -
Alphabetical order is
a-z, not priority.prod.auto.tfvarscomes afterdev.auto.tfvars(letter d < p in ASCII) but beforeqa.auto.tfvars. This has nothing to do with "importance". -
.tfvars does not support variables. You cannot write
env = var.somethinginside a .tfvars file. Only literal values and complex structures are allowed. -
JSON and HCL cannot be mixed in one file. A file is either
.tfvarsor.tfvars.json. Both formats can coexist in the same directory; Terraform reads them in sequence. -
-var-filedoes not fail on a missing file. If a CI job omits the file, Terraform prompts for values interactively (or fails with-input=false). Always pass-input=falsein CI. -
.tfvars is not the right place for secrets in open-source projects. Even when listed in .gitignore, files can be committed by accident or leak through CI logs. Real secrets belong in environment variables (
TF_VAR_*) or a secrets manager.