What Is a Terraform tfvars File?
A .tfvars file is where you set the actual values for your Terraform variables. Your variables.tf declares what variables exist and what types they are. Your .tfvars file fills in the values for a specific environment or context.
Think of it as the difference between a form template and a filled-in form. variables.tf is the blank form with field names and rules. prod.tfvars is the form filled out with production values.
At Razorpay, the CI/CD pipeline runs terraform apply -var-file=prod.tfvars for production and terraform apply -var-file=dev.tfvars for development. The infrastructure code is identical — only the variable files differ. When a new environment is needed, create a new .tfvars file.
+------------------------------------------+| variables.tf (declarations) || variable "environment" { type = string } || variable "instance_type" { type = string}|| variable "replica_count" { type = number}|+------------------------------------------+ | +-----------+-----------+ | | v v+------------------+ +-------------------+| dev.tfvars | | prod.tfvars || environment | | environment || = "dev" | | = "prod" || instance_type | | instance_type || = "t3.micro" | | = "t3.large" || replica_count | | replica_count || = 1 | | = 3 |+------------------+ +-------------------+tfvars File Format
# terraform.tfvars — basic syntax # Stringsenvironment = "dev"aws_region = "ap-south-1"project_name = "razorpay-payments" # Numbersreplica_count = 2rds_allocated_storage = 20 # Booleansenable_multi_az = falseenable_deletion_protection = false # Listsavailability_zones = ["ap-south-1a", "ap-south-1b"]allowed_cidr_blocks = [ "10.0.0.0/8", "172.16.0.0/12",] # Mapsinstance_types = { dev = "t3.micro" staging = "t3.small" prod = "t3.large"} # Objects — match the variable type exactlydatabase_config = { engine = "postgres" engine_version = "14.9" instance_class = "db.t3.micro" allocated_storage_gb = 20 multi_az = false backup_retention_days = 3}Auto-Loading vs Explicit Loading
# These files are auto-loaded — Terraform reads them without being toldterraform.tfvars # always auto-loaded if presentterraform.tfvars.json # JSON format, also auto-loaded*.auto.tfvars # any file ending in .auto.tfvars# e.g.: common.auto.tfvars, secrets.auto.tfvars # Explicit load — you must pass these with -var-fileterraform plan -var-file=dev.tfvarsterraform plan -var-file=prod.tfvarsterraform apply -var-file=staging.tfvarsPer-Environment File Pattern
# Recommended directory structure.├── main.tf├── variables.tf├── outputs.tf├── versions.tf├── terraform.tfvars # default/dev values (auto-loaded)├── dev.tfvars # override for dev├── staging.tfvars # override for staging├── prod.tfvars # override for prod└── terraform.tfvars.example # template showing all required variables# CI/CD pipeline# Dev deployterraform apply -var-file=dev.tfvars # Staging deployterraform apply -var-file=staging.tfvars # Production deployterraform apply -var-file=prod.tfvarsWhat NOT to Put in tfvars Files
# BAD — do not put secrets in tfvars files committed to Gitdb_master_password = "S3cr3tP@ss" # NEVER in .tfvars committed to Gitapi_key = "sk-abc123xyz" # NEVER in .tfvars committed to Git # GOOD — use environment variables for secrets# export TF_VAR_db_master_password="S3cr3tP@ss"# export TF_VAR_api_key="sk-abc123xyz"# Then terraform apply reads TF_VAR_ automaticallyThe .tfvars.example Pattern
# terraform.tfvars.example — commit this to Git# Shows all required variables without values# New engineers copy this and fill in their values aws_region = "" # e.g., "ap-south-1"environment = "" # dev, staging, or prodproject_name = "" # e.g., "razorpay-payments" database_config = { engine = "postgres" # do not change engine_version = "14.9" # do not change instance_class = "" # e.g., "db.t3.medium" allocated_storage_gb = 20 multi_az = false # set true for prod backup_retention_days = 7} # db_master_password — DO NOT set here# Set via: export TF_VAR_db_master_password="your-password".gitignore Rules for tfvars Files
# .gitignore — what to exclude # Never commit state filesterraform.tfstateterraform.tfstate.backup*.tfstate*.tfstate.backup # Never commit the .terraform directory (downloaded providers/modules).terraform/.terraform.lock.hcl # DO commit this — it locks provider versions # Never commit auto.tfvars files that may contain secrets*.auto.tfvars*.auto.tfvars.json # You CAN commit terraform.tfvars if it has no secrets# You CAN commit dev.tfvars, staging.tfvars, prod.tfvars if they have no secrets# You MUST commit terraform.tfvars.example for documentationVariable Precedence — Where tfvars Files Fit
Priority order (highest wins): 1. -var flag on command line terraform plan -var="environment=prod" 2. TF_VAR_ environment variables export TF_VAR_environment="prod" 3. *.auto.tfvars (alphabetical) common.auto.tfvars 4. terraform.tfvars (auto-loaded) 5. default values in variable {} blocks variable "environment" { default = "dev" }Troubleshooting tfvars Files
| Error | Root Cause | Fix |
|---|---|---|
Error: No value for required variable |
Variable has no default and not in any tfvars | Add value to tfvars or set TF_VAR_ |
Error: Invalid expression |
Syntax error in tfvars | Check for missing quotes or commas |
Error: Variables not allowed |
Trying to use var.x inside tfvars |
tfvars values are literals, not expressions |
| Values not loading | Wrong filename or wrong directory | Confirm filename ends in .tfvars, run from correct directory |
| Object type mismatch | tfvars object has wrong keys | Match keys exactly to the object({}) type in variables.tf |
REMEMBER THIS**Remember:** Commit `terraform.tfvars.example` to Git as a template, but be careful with `terraform.tfvars` — auto-load means it applies to every `terraform plan` and `apply` run in that directory. If it has environment-specific values, switching environments requires editing or replacing the file.
COMMON MISTAKE / WARNING**Common Mistake:** Creating `.auto.tfvars` files for secrets. Any file ending in `.auto.tfvars` is loaded automatically — if you create `secrets.auto.tfvars` with passwords and commit it to Git, those secrets are now in your repository history permanently. Use `TF_VAR_` environment variables for all secret values.