← Back to Blog RSS

Terraform tfvars: The complete guide (with examples)

Terraform Infrastructure DevOps

A terraform tfvars file is the standard way to keep variable values out of your .tf files, which is something you definitely want to do. However, it’s not a complete solution for secrets management, drift detection, or variable sprawl across a growing set of workspaces.

TL;DR
$ cat terraform-tfvars.tldr
• A tfvars file assigns values to variables declared elsewhere in your configuration, so the same code can run against different environments.
• Terraform automatically loads terraform.tfvars and any *.auto.tfvars file, no flag required.
• tfvars files are plain text, so they are the wrong place for API keys, passwords, or anything else that counts as sensitive data.
• Where tfvars sits in Terraform's precedence order is one of the most common sources of confusion, and one of the easiest things to get wrong.

Every Terraform configuration eventually needs to run more than once with different inputs. The same module deploys a staging environment on Monday, and a production environment on Friday, and you should not need to edit the .tf files themselves in between.

Whether your root module deploys a single service or coordinates multiple modules across several environments, you need to know where the values that change between runs actually live.

Enter Terraform tfvars files. They separate the shape of your infrastructure – the resources, the relationships between them, and the logic – from the specific values that make one deployment different from another.

This article covers what a tfvars file actually is, how Terraform loads and prioritizes it, and what a working example looks like with a few different variable types.

It also covers where tfvars stops being enough – after all, a file full of key-value pairs cannot tell you whether your live infrastructure still matches what it says, and it was never built to hold a secret safely.

You'll have a clear, working understanding of terraform.tfvars, and a realistic sense of what still needs to sit on top of it.

What is a tfvars file?

A tfvars file assigns values to input variables declared with variable blocks somewhere in your configuration, typically inside a Terraform file such as variables.tf.

The variable block defines the name, the type, and often a default value or a description, which together make up your variable definitions. The tfvars file supplies the actual value you want to use for a given run, without touching the configuration itself.

Their entire purpose is defining values for variables you have already declared elsewhere, rather than declaring anything new.

.tfvars files are readable, work with any Terraform command, and do not require installing anything new to get started, meaning they're a preferred first solution for developers and engineers.

Terraform accepts two formats for these files:

Terraform also draws a distinction between files it loads automatically and files you have to point it at. A file named terraform.tfvars, or any file ending in .auto.tfvars or .auto.tfvars.json, gets loaded without any flag on the command line. Anything else (a file named prod.tfvars, for example) only gets loaded if you tell Terraform to load it.

How do tfvars files work?

The mechanics of .tfvars files are simple.

A .tfvars file is a list of key-value pairs, where each key matches the name of a variable declared in a variable block somewhere in your root module.

Terraform reads the file, matches each key against a declared variable, and assigns the value.

Defining variables in this way is what makes managing variables, and managing Terraform variables, more specifically, achievable, from a small set of files rather than scattered throughout your configuration.

Here is a terraform.tfvars example showing three plain string assignments:

region = "us-east-1"
instance_type = "t3.micro"
environment = "staging"

Each of these lines only works if a matching variable declaration exists elsewhere in the configuration. If you assign a value to a name Terraform does not recognize (an undeclared variable in other words), Terraform warns you about it rather than accepting it, making it a useful guardrail against a typo turning into a missing setting.

A variable without a default value counts as one of your required variables. Terraform will prompt for a value, or fail in a non-interactive run, if nothing is supplied.

Adding a default to the variable block makes that variable optional instead, and the .tfvars file only needs to override it when a run actually calls for something different.

Bear in mind, however, that Terraform does not evaluate function calls or references to other variables inside a .tfvars file. You cannot write instance_type = var.default_type inside a .tfvars file, and you cannot call a function like upper() to transform a value. Expressions built from literal values alone do work, so 1 + 2 resolves to 3, but anything that reaches outside the file is rejected.

A Terraform tfvars example

A single string assignment only shows part of the picture. The following example pairs variable declarations with the .tfvars values that satisfy them, covering most of the shapes you need for all the variables a typical module might take.

The variable block declarations, likely living in a variables.tf file:

variable "environment" {
type = string
description = "Deployment environment name"
}
variable "availability_zones" {
type = list(string)
description = "List of availability zones to deploy into"
}
variable "tags" {
type = map(string)
description = "Common tags applied to all resources"
default = {}
}

And the matching .tfvars file:

environment = "staging"
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
tags = {
Team = "platform"
Project = "checkout-service"
}

This pairing covers the three shapes you will run into most often:

Terraform supports more complex types too, including object types that combine several fields together, but strings, lists, and maps cover the majority of real configuration.

Terraform tfvars file example

The examples above show the syntax in isolation. A realistic terraform .tfvars file example usually belongs to a specific environment and gets named accordingly rather than relying on the auto-loaded terraform.tfvars file.

A file named prod.tfvars might look like this:

region = "us-east-1"
instance_type = "t3.large"
environment = "production"
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
enable_monitoring = true
tags = {
Team = "platform"
Environment = "production"
ManagedBy = "terraform"
}

Because prod.tfvars is not named terraform.tfvars and does not end in .auto.tfvars, Terraform will not load it on its own. You pass it in explicitly with the -var-file flag, using the following command on the Terraform CLI:

terraform apply -var-file="prod.tfvars"

You can also use this method to pass values from more than one source in a single run.

Terraform accepts multiple -var-file flags in one command, so teams often split configuration into multiple files, a shared variable file plus an environment-specific one, rather than cramming everything into a single .tfvars file.

Naming your environment-specific files explicitly, rather than relying on auto-loading, means a plan or apply against production requires a deliberate flag rather than happening because a file happened to sit in the working directory. Teams managing multiple environments from the same root module tend to prefer this explicit pattern.

Variable precedence order

Terraform supports several different methods of setting variables, and variable precedence is what trips people up the most because only one source wins when there is a conflict.

Terraform resolves a variable's value in this order of lowest to highest priority. Default values set inside the variable block sit at the bottom, used only when nothing else supplies a value.

Above that sit environment variables, sometimes called environmental variables, set with a TF_VAR_ prefix matching the variable name.

Above environment variables sits the terraform.tfvars file (if one exists). Above that is terraform.tfvars.json, if both exist in the same directory.

Then, above terraform.tfvars.json you will find any *.auto.tfvars or *.auto.tfvars.json files, evaluated in lexical order when there is more than one.

At the very top sit -var and -var-file flags passed on the command line, which are evaluated in the order you provide them and always take precedence over every other source.

Essentially, command-line flags always win, while a value quietly sitting in an environment variable will lose to almost every file-based source if there is a conflict.

Many confused debugging sessions have come out of this priority, where someone sets TF_VAR_environment for a CI job, forgets that a terraform.tfvars file also sets environment, and spends twenty minutes wondering why the environment variable does not seem to take effect.

This precedence order is fixed and does not vary based on how a variable is declared, only where its value comes from.

Here is a concrete case. Say a variable named instance_type has a default of t2.micro in its variable block, a value of t3.medium set through TF_VAR_instance_type, and a value of t3.large sitting in terraform.tfvars.

Without any command-line flag, Terraform uses t3.large, because the .tfvars file outranks the environment variable, which in turn outranks the default.

Add -var="instance_type=t3.xlarge" to the terraform apply command, and that value wins over everything else, regardless of what any file or environment variable says.

Common use cases

For the most part, teams use .tfvars files to separate environment-specific values, dev, staging, and production most commonly, without duplicating the configuration itself three times over.

The .tf files describe the shape of the infrastructure once, and separate variable files supply variables specific to each environment.

A closely related use case is keeping non-sensitive configuration values out of version-controlled .tf files entirely, so that changing an instance type or a region does not require touching code that describes resources and their relationships.

.tfvars files also simplify CI/CD pipelines considerably.

A pipeline job can point -var-file at a different file depending on which environment it is deploying to, without needing separate Terraform configurations or complex conditional logic baked into the code itself.

It applies whether you are calling a single Terraform module or coordinating several modules across a larger repository, since the Terraform code itself never changes; only the file referenced by -var-file changes.

Meanwhile, there is also a benefit to teams running the same module against several regions or several customer accounts, where the module itself never changes but the specific inputs, region names, instance sizes, and tagging conventions do.

A well-organized folder structure with one .tfvars file per target helps you manage that repetition without duplicating a single line of the underlying .tf files.

The limitations of tfvars

.tfvars files earn their place for assigning values, but they have a number of limitations, so avoid treating a .tfvars file as a complete solution.

Secrets

A .tfvars file is plain text, stored on disk, and often committed to version control by accident even when teams know better.

Putting an API key, a password for an RDS database instance, or any other sensitive value directly into a .tfvars file means that value now lives unencrypted wherever the file travels.

Terraform lets you mark sensitive variables so their values are hidden from CLI output, but that only protects what gets printed to your terminal, not the .tfvars file sitting on disk.

Environment variables or a dedicated secrets manager are a better home for anything that counts as confidential data. If a .tfvars file must contain something sensitive for a specific workflow, keeping that file out of version control entirely is the minimum precaution.

Drift

A .tfvars file only affects the next terraform plan or terraform apply. It has no impact on what is actually deployed right now.

If someone changes a value in a .tfvars file but never runs an apply, or if infrastructure changes outside of Terraform entirely, the .tfvars file and the real world quietly disagree with each other, and nothing within the file itself will tell you that has happened.

Unchecked, these files can cause drift.

Sprawl

A single .tfvars file per environment is manageable. Dozens of .tfvars files spread across many Terraform workspaces, each with slightly different keys, some missing values that others have, become genuinely error-prone – particularly as a team grows and more people are editing them without full context on every other file.

Process

A .tfvars file does not enforce who is allowed to change a given value, nor does it require an approval step before that change reaches an apply.

A .tfvars file is just a file.

Whatever review process exists around it has to be built separately, usually through version control conventions and pull request rules rather than anything Terraform itself understands.

Pros Cons
Simple, no extra tooling required Not encrypted or access-controlled
Works with any Terraform setup, regardless of scale No drift detection of its own
Easy to template per environment, nothing more than a text editor is needed No built-in audit trail of who changed a value, or when
Does not scale cleanly across dozens of .tfvars files spread over multiple workspaces without added structure.

The strongest case for .tfvars files is simplicity. They require no extra tooling, work with any Terraform setup regardless of scale, and are easy to template per environment using nothing more than a text editor.

The honest tradeoffs sit on the other side of that same simplicity. .tfvars files are not encrypted or access-controlled. They offer no drift detection of their own. There is no built-in audit trail showing who changed a given value or when. And they do not scale cleanly once a team is managing dozens of .tfvars files across multiple workspaces without additional structure layered on top.

Where Stategraph fits in

.tfvars files solve a specific, narrow problem well. They assign values so the same configuration can run against different environments. What they do not solve is the bigger question sitting underneath variable management entirely: whether your actual infrastructure still matches what your configuration and your state say it should.

Stategraph is a graph-based Terraform state backend, effectively a remote backend built around understanding the relationships between resources and the state that describes them, not around helping teams manage Terraform variables.

Stategraph sits at the layer above .tfvars, helping teams see how resources connect across a state file, catch drift between what state says and what is actually deployed, and manage state safely across multiple workspaces as infrastructure grows.

A .tfvars file can tell Terraform which instance type or region to use on the next apply. It cannot tell you whether last month's manual change to a security group still matches what your configuration expects, or whether a variable change in one workspace has consequences for a resource that depends on it in another. That is the reliability and visibility problem Stategraph solves.

Conclusion

A terraform .tfvars file is a simple, genuinely useful tool for assigning values to terraform variables, separating environment-specific configuration, and keeping your .tf files reusable across dev, staging, and production.

It is not a secrets manager, it does not detect drift, and it was never meant to scale cleanly across dozens of workspaces on its own.

Understanding where .tfvars sits in Terraform's priority order (and understanding where its responsibilities end) is what keeps a growing Terraform setup manageable rather than confusing. For teams looking at the layer above variable management, where state reliability and visibility actually live, the Stategraph docs are a reasonable next stop.

Terraform tfvars FAQs

What is the difference between terraform.tfvars and variables.tf?

A variables.tf file, or any file containing variable blocks, declares what variables exist, their types, and often a default value.

A terraform.tfvars file assigns the actual values to use for those already-declared variables. One defines the interface, the other fills it in.

Can you use multiple tfvars files at once?

Yes. Terraform loads terraform.tfvars automatically, along with any *.auto.tfvars files, and you can also pass multiple -var-file flags on the command line. When more than one file sets the same variable, Terraform's precedence order decides which value wins.

Does terraform.tfvars get loaded automatically?

Yes. A file named exactly terraform.tfvars, or any file ending in .auto.tfvars or .auto.tfvars.json, loads automatically without a flag. Files with any other name, such as prod.tfvars, need to be passed explicitly with -var-file.

Should tfvars files be committed to version control?

Generally, yes, particularly for non-sensitive configuration values, as this is exactly what makes environment-specific values reviewable and repeatable across a team. Any .tfvars file containing sensitive values should be excluded from version control instead.

What happens if a variable is set in both tfvars and the command line?

The command line wins. -var and -var-file flags sit at the top of Terraform's precedence order, above every auto-loaded tfvars file, so a value passed on the command line always overrides whatever terraform.tfvars or an .auto.tfvars file assigns for the same variable, whether you run terraform plan or apply. Between the flags themselves the last one on the line wins, so a -var-file placed after a -var overrides it.