What does `nullable = false` do on a Terraform input variable, and how does it change what happens when a caller explicitly passes `null` to a variable that has a default?
answer
- null is a value, not an omission
- default fills omission only
- nullable defaults to true
- false makes null fall back to default
- no default plus null means error
basics
~20 snullable = false forbids the value being null inside the module. With the default nullable = true, an explicit null overrides the default and the module sees null; with nullable = false, Terraform substitutes the default instead, or errors if there is none.
solid answer
~50 s`nullable` controls whether a caller may assign `null` to the variable, and it defaults to `true`. That default has a consequence people find surprising: `null` is a real value, not an absence, so passing it explicitly *overrides* the variable's `default` and the module sees `null` rather than the default. Setting `nullable = false` says the value can never be null inside the module — Terraform then treats an explicitly passed `null` as if the argument were omitted and substitutes the `default`, and raises an error if there is no default to substitute. Which you want depends on the module. Keep the default `nullable = true` when `null` is meaningful — typically a pass-through argument where null means "do not set this" — and use `nullable = false` when the module's own expressions would break on a null, so the constraint is enforced at the boundary instead of failing somewhere inside. The argument was added in Terraform 1.1.
code
hcl · 14 linesvariable "retention_days" {
type = number
default = 30
nullable = false
}
variable "kms_key_arn" {
type = string
default = null
}
output "retention" {
value = var.retention_days
}go deeper
Remember that null is a value a caller can pass, not the same as leaving a variable out, and that default fills only the omission case.
Explain the switch: with nullable = true an explicit null overrides the default, while nullable = false substitutes the default instead and errors when no default exists.
Show the design call per input — keep null meaningful for pass-through arguments where it means "do not set this", and forbid it where the module's own expressions would break far from the caller.
Own the boundary principle: enforce shape guarantees at the module's edge so failures name an input, not an internal expression, and keep that contract stable for consumers you cannot see.
## Three distinct states, not two A module input can be in one of three states, and conflating them is the source of the confusion here: 1. **Omitted** — the caller wrote nothing. The variable's `default` applies; with no `default`, the variable is required and Terraform will not proceed without a value. 2. **Set to `null`** — the caller explicitly assigned `null`. This is a *value*, not an omission. 3. **Set to a value** — the ordinary case. The `nullable` argument decides what state 2 means. ## Default behaviour: `nullable = true` ```hcl variable "kms_key_arn" { type = string default = "arn:aws:kms:...:key/fallback" } ``` With `nullable` left at its default of `true`, a caller writing `kms_key_arn = null` gets exactly that: the module sees `null`, and the declared `default` is *not* applied. The default only fills an omission, and `null` is not an omission. This is deliberate and useful, because `null` has a specific meaning for a resource argument: assigning `null` to an argument tells Terraform to behave as if the argument were not written at all, leaving the provider's own behaviour in charge. A module that forwards optional arguments straight through relies on being able to receive `null`: ```hcl resource "aws_s3_bucket_server_side_encryption_configuration" "this" { # ... } ``` Generally: any input whose job is "set this, or don't" wants to stay nullable. ## `nullable = false` ```hcl variable "retention_days" { type = number default = 30 nullable = false } ``` This declares that the value is never null inside the module. Two consequences follow. A caller who passes `null` does not get null — Terraform substitutes the `default` (`30`) instead, treating the null as an omission. And if the variable had no `default`, passing `null` is an error rather than a silent hole. That guarantee is worth having whenever the module's own code would misbehave on a null: arithmetic, string interpolation into a name, `length()`, or a `for` expression over a collection. Without it, a null threads its way inside and fails somewhere far from the caller, with an error message about an expression rather than about an input. ## Choosing between them The question to ask about each input is: *does null mean something here?* - **Yes** — the value is optional in the underlying API and the module forwards it. Leave it nullable, and let `null` carry the meaning "unset". - **No** — the module always needs a usable value. Set `nullable = false` and pair it with a `default`, so the module has a single, guaranteed shape to work with. As a module interface decision this sits alongside default-versus-required. A variable with no `default` is required, and that is the right call when there is no safe value to guess — a region, an environment name, a VPC id. A variable with a `default` is optional, and the default is a statement about what the module thinks the sensible choice is. Adding `nullable = false` on top of a default closes the last hole: the module cannot be handed nothing. ## A related shape: object attributes Inside an `object({...})` type constraint, the equivalent lever is `optional(T)` versus `optional(T, default)` — the one-argument form yields `null` for an omitted attribute and the two-argument form yields the default. `nullable` operates on the variable as a whole, not on individual attributes of its type, so a nullable-false object variable can still contain null attributes. ## Version note `nullable` was added to the `variable` block in Terraform 1.1. On older versions the argument is not recognised, so a module using it should carry a `required_version` constraint that reflects that.
- Why would a module author deliberately give a variable the default value null?To express "unset" as the default. Assigning `null` to a resource argument makes Terraform behave as if the argument were not written, leaving the provider's own default in charge. A variable defaulting to `null` and forwarded straight to such an argument gives the caller an opt-in knob without the module having to guess a value.
- What is the difference between a variable being required and being nullable = false?They constrain different things. Required means there is no `default`, so the caller must supply something. `nullable = false` means whatever arrives cannot be null inside the module. A variable can be optional and non-nullable — a default plus `nullable = false` — which guarantees a usable value while letting the caller stay silent.
saying these in an interview costs you the question
- Believing null and omitted are the same thing
- Expecting an explicit null to fall back to the default by default
- Thinking nullable = false makes the variable required
- Assuming nullable rejects empty strings and empty lists too
- Using nullable on individual object attributes