Terraform locals are named, module-scoped values that let you assign an expression once and reuse it throughout your configuration, reducing duplication and making your code easier to read and maintain. If you have ever found yourself repeating the same tag block, name prefix, or computed value across multiple resources, locals are the solution. Unlike input variables, locals can’t be overridden from outside the module, which makes them ideal for intermediate values derived from other resources, data sources, or expressions. Locals are pure computed expressions inside a module, no provider calls, deterministic given inputs, used for reuse and clarity, not overrideable by callers.
The practical effect is immediate for teams working with infrastructure as code. Repetition creates drift risk. When a naming convention or tag set is repeated in twenty resource blocks, a single change requires twenty edits, and the probability of an inconsistent edit rises with every manual touch. Locals centralize that logic. The readability gain is not cosmetic; it is operational safety. A reader opening a module can find the canonical definition of a name prefix, a common tag map, or a computed suffix in one declarative block and then trust that every downstream reference resolves to that single source. This also improves review velocity because reviewers compare a change against one authoritative definition rather than scanning for scattered duplicates.
The comparison to a general programming language construct is a local temporary variable declared within a function. It lives inside the module boundary, it is initialized from inputs or other expressions, and it is consumed within that same lexical scope. That analogy helps developers from application backgrounds understand why locals are not a mechanism for external configuration. They are an internal refactor tool.
What Terraform Locals Are and How They Differ From Input Variables
Locals are named values that can be assigned and used in your code. They mainly serve the purpose of reducing duplication within the Terraform code. When you use locals in the code, since you are reducing duplication of the same value, you also increase the readability of the code.
All programming languages have a way to express and store values within the context of a code block. In the case of Terraform configurations, that functionality is delivered through Terraform local values. These allow you to define temporary values and then reference them elsewhere in the configuration.
Local values, often called locals or Terraform local variables, can be used to store an expression that will be referenced multiple times, perform data transformation from other sources, or store static values to be used in the configuration.
Locals are one of three varieties of Terraform variables that can be used to request or public values, with the other two being input variables and output values. This taxonomy matters because it places locals alongside variables and outputs as first-class mechanisms for value flow, while emphasizing a different lifecycle and control surface.
The first difference can be pointed towards the scope. A Local is only accessible within the local module vs a Terraform variable, which can be scoped globally. Another thing to note is that a local in Terraform doesn’t change its value once assigned. A variable value can be manipulated via expressions. This makes it easier to assign expression outputs to locals and use them throughout the code instead of using the expression itself at multiple places.
Locals are declared with the locals block plural but referenced with local dot name singular. This is the most common source of confusion.
Impact of Scope and Immutability
Because locals cannot be overridden from outside the module, they enforce a contract. Callers can influence a module through input variables, and the module can derive internal constants from those inputs using locals. That derivation is hidden from callers and cannot be tampered with at call time. The impact is predictable composition. A module author can refactor internal implementation details without breaking the public interface.
Immutability after assignment means a local is a computed constant for the duration of the plan and apply. That guarantees that any resource referencing local.x will see the same value everywhere within the module. The real-world consequence is elimination of subtle mismatches where the same expression is written twice with slight variations.
Declaring and Referencing Locals in Configuration
Defining local values in Terraform code is done using a locals block, with each local assigned a name and value in the format of a key-value pair.
The following code creates a local value called environment and assigns it the string value development.
locals {
environment = "development"
}
Locals can be assigned any valid Terraform data type, such as a string, list, map, or object.
A canonical example of a local declaration in a Terraform script is:
locals {
bucket_name = "${var.text1}-${var.text2}"
}
When you use locals in the code, since you are reducing duplication of the same value, you also increase the readability of the code.
Reference syntax is local.name. The singular reference contrasts with the plural declaration block. Teams adopting locals should enforce this naming discipline in code reviews to avoid the common error of referencing locals.name or locals{ }.
Data Types, Functions and Expressions Inside Locals
Locals fully support built-in functions, so you can use expressions like merge(), concat(), format(), or toset() directly in a local declaration and reference the result throughout your module.
Using Terraform locals means you define a value or expression once and reference it everywhere it’s needed. If it changes, you update it in one place instead of hunting down every occurrence across your configuration.
The ability to combine locals with variables for dynamic values is central to practical use. A variable provides external input, often environment specific. A local derives a canonical form from that input, such as a prefixed resource name or a merged tag map.
For example, a module may accept var.project and var.environment as inputs and derive:
locals {
name_prefix = "${var.project}-${var.environment}"
common_tags = merge(var.tags, { Environment = var.environment })
}
All resources can then reference local.nameprefix and local.commontags. Changing the naming convention requires editing the local definition only.
Data Type Support
The reference facts explicitly support string, list, map, and object as assignable types. This covers the majority of configuration patterns:
- Strings for name prefixes, bucket names, and formatting
- Lists for sets of subnets, security group IDs, or instance types
- Maps for tag collections or attribute bundles
- Objects for structured records with named attributes
Because locals are expressions, they can be built from functions and from other locals. The composition chain remains deterministic given inputs, which reinforces predictable plans.
Practical Impact of Locals on Readability and DRY Maintenance
Why use Terraform locals instead of repeating values?
Using Terraform locals means you define a value or expression once and reference it everywhere it’s needed. If it changes, you update it in one place instead of hunting down every occurrence across your configuration.
The operational impact is measurable in change management. A tag policy update that would require editing dozens of resources becomes a single edit to a locals block that defines common_tags. The plan output then shows the change propagated consistently. This reduces human error and shortens review time.
Readability improves because the intent of the configuration becomes explicit. Instead of seeing a long interpolation repeated, a reviewer sees a named local like local. naming_convention and can jump to its definition. The code communicates purpose rather than mechanics.
Locals Versus Variables Versus Tfvars Versus Data Sources
Understanding the ecosystem around locals requires comparing related mechanisms.
Locals vs Input Variables vs Tfvars
What is the difference between locals and tfvars?
Locals are computed constants inside a module, derived from expressions and not overrideable by callers, useful for keeping logic DRY and readable. Tfvars files provide values for input variables from outside the module, enabling environment specific overrides through Terraform’s variable precedence and clean separation of config from code.
The distinction is control flow. Tfvars is an input mechanism. Locals are an internal computation mechanism.
A local module in Terraform is a reusable module loaded from your repo via a filesystem path, not the Registry or a remote VCS. Reference it with a path in the source field, for example source = "./modules/network". It enables reuse without versioning boundaries, so callers pick up changes immediately.
The table below summarizes the contrast.
| Aspect | Locals | Input Variables | Tfvars Files |
| Scope | Module internal only | Module public interface | External value supply |
| Overrideable | No | Yes via call or tfvars | N/A, provides values |
| Purpose | DRY intermediate values | External configuration | Environment specific values |
| Determinism | Pure expression, no provider calls | Can be any value | Can be any value |
Locals vs Data Sources
What is the difference between data and locals in Terraform?
Data sources fetch read-only information from providers or remote state during planning or apply, so values can change outside Terraform and require provider access.
Locals never call providers. They are pure computed expressions inside a module. Data sources are dynamic and external. Locals are static within a run once inputs are known.
The practical implication is that locals should never be used to hide provider calls. If a value requires querying a remote system, it must be a data source. Locals can transform the result of a data source, but they cannot replace it.
Practicing Terraform Locally Without Cloud Providers
Practicing Terraform locally offers several advantages, especially for those who are new to infrastructure as code or want to experiment without incurring cloud costs.
Key benefits include:
- Cost-Effective Learning: You can experiment and learn Terraform without the need for cloud resources, which can be costly.
- Rapid Iteration: Local environments allow for quick testing and iteration, speeding up the learning process.
- Isolated Environment: A local setup provides a sandboxed environment where you can make mistakes without affecting production systems.
- No Cloud Dependencies: You can practice Terraform without needing access to cloud providers, making it accessible to everyone.
- Realistic Environment: Using tools like kind allows you to simulate a real Kubernetes cluster locally.
Kind is a lightweight tool for running local Kubernetes clusters using Docker containers as nodes.
Before diving into Terraform configurations, you can set up a local Kubernetes cluster using kind. This will provide the foundation for a Terraform practice environment. Local practice removes the cost and risk of cloud resources while preserving realistic dependency graphs and provider interactions through local emulators.
The Local Provider for Managing Local Resources
The Local provider is used to manage local resources, such as files.
Note Terraform primarily deals with remote resources which are able to outlive a single Terraform run, and so local resources can sometimes violate its assumptions. The resources here are best used with care, since depending on local state can make it hard to apply the same Terraform configuration on many different local systems where the local resources may not be universally available. See specific notes in each resource for more information.
Official documentation on how to use this provider can be found on the Terraform Registry.
The Local provider is conceptually distinct from Terraform locals. The provider manages files and directories on the machine running Terraform. Locals are configuration values. The provider is a resource type. The name similarity causes confusion, but the semantics are different: one is a value scoping mechanism, the other is a provider for local filesystem operations.
Common Errors and Misunderstandings
Common locals errors stem from scope confusion and naming mismatch.
The most common source of confusion is the plural block name locals versus the singular reference local.name.
Another frequent mistake is attempting to override a local from a caller. Because locals can’t be overridden from outside the module, any attempt to set a local via module arguments will fail. The correct pattern is to expose an input variable and derive the local from it.
Using locals for values that require provider data is another error. Locals are pure computed expressions inside a module, no provider calls. If a value must be fetched, a data source is required.
Conclusion
Terraform locals are a module-scoped mechanism for defining named computed constants that reduce duplication, increase readability, and enforce internal consistency. They differ from input variables by being non-overrideable and confined to the module, and they differ from tfvars by being internal derivations rather than external inputs. They differ from data sources by being provider-free pure expressions.
The practical value emerges in maintenance velocity and safety. Centralizing repeated expressions into locals makes policy changes atomic and reviews focused. The combination of variables for external input, locals for internal derivation, and outputs for result exposure creates a clean separation of concerns.
Practicing Terraform locally with tools like kind preserves the learning benefits without cloud costs, while the Local provider offers a separate capability for managing local files with the caveat that local state can undermine Terraform’s remote state assumptions.
OpenTofu is an open-source version of Terraform that expands on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6.
Terraform Management Made Easy is described by Spacelift as effectively managing Terraform state, more complex workflows, supports policy as code, programmatic configuration, context sharing, drift detection, resource visualization and includes many more features.
These ecosystem points reinforce why mastering locals matters: as workflows become more complex and teams adopt policy as code and drift detection, the clarity provided by well-named locals reduces the surface area for errors across larger module graphs.