Vars

A var "<name>" { ... } block resolves a string at config-load time from an environment variable, the HEAD commit of a local git checkout, or a literal fallback, and makes it referenceable as ${var.<name>}. Vars are evaluated before resources, so the resolved value is just another string by the time a resource attr reads it.

var "app_tag" {
  from_env     = "APP_TAG"
  from_git_sha = "../app"
  default      = "dev"
}

resource "docker_container" "app" {
  host  = host.primary.addr
  image = "app:${var.app_tag}"
  pull  = false
}
var_block ::= "var" string "{" attr* "}"

The single label is the var's name. Names must be unique within the document — duplicates across -c files error with duplicate var.

The problem it solves: mutable tags defeat the diff

Stratum decides whether to recreate a docker_container by diffing its desired attrs against the last applied state. A locally-built image tagged app:latest defeats this: rebuilding the image moves what latest points to, but the container's image = "app:latest" attr never changes, so the plan diff sees a NoOp and the container keeps running the stale image. The only escape was stratum apply -r to force a recreate — blunt, and with its own ordering pitfalls.

A var sourced from the build's git SHA fixes this declaratively. Derive an immutable tag from the source checkout's HEAD and reference the same ${var.app_sha} in both the build command and the container image:

var "app_sha" {
  from_git_sha = "../app"
  from_env     = "APP_SHA"
}

resource "ssh_exec" "app-build" {
  host       = host.primary.addr
  command    = "cd /srv/repos/app && docker build -t app:${var.app_sha} ."
  depends_on = ["ssh_exec.app-pull"]
}

resource "docker_container" "app" {
  host       = host.primary.addr
  image      = "app:${var.app_sha}"
  pull       = false
  depends_on = ["ssh_exec.app-build"]
}

A new commit moves HEAD, so ${var.app_sha} resolves to a new value, so both the command and the image attrs change. The normal plan diff then re-runs the build and recreates the container in dependency order, in a single apply — no -r, no mutable :latest. The pull = false is required because the image is built on the host and isn't in any registry (see docker_container: pull = false).

Sources

At least one of from_env, from_git_sha, or default is required. A block with none of them is a hard error (BadVarBody).

attrtypedescription
from_envstringName of an environment variable. Used when set to a non-empty value; an unset or empty value falls through to the next source.
from_git_shastringPath to a local git checkout. Runs git -C <path> rev-parse [--short] HEAD and uses the resulting SHA. Relative paths resolve against the .strat file's directory (same rule as secret from_file). A failed git invocation falls through to the next source.
shortboolDefault true. Controls the SHA width for from_git_sha: true passes --short (7-ish chars), false uses the full 40-char SHA. No effect on the other sources.
defaultstringLiteral fallback used when no earlier source yields a value.

Resolution precedence

Sources are tried in a fixed order and the first one that yields a value wins:

  1. from_env — if the named env var is set and non-empty.
  2. from_git_sha — if git rev-parse succeeds and returns a hex SHA.
  3. default — the literal fallback.

A source that is specified but yields nothing (env var unset or empty, git checkout missing or rev-parse failing) falls through to the next source rather than erroring. If no source yields a value and there is no default, resolution is a hard error and the load aborts — vars are fail-closed:

var `app_sha`: could not resolve — no source yielded a value (tried: from_env `APP_SHA`, from_git_sha `../app` (...))
  fix: set a working `from_env`/`from_git_sha`, or add a `default = "..."`

This means from_env overrides the git SHA — useful in CI, where the pipeline already knows the exact commit it pushed and can pass it directly (APP_SHA=<sha> stratum apply -y) without relying on a local checkout being present.

from_git_sha tracks committed HEAD only

git rev-parse HEAD reads the committed tip of the current branch. A dirty working tree — uncommitted edits, staged-but-uncommitted changes — does not affect the resolved SHA. The var only moves when you commit.

This matters for the git-SHA tag pattern above. If the build host pulls the source with git reset --hard origin/main (the usual idempotent-checkout shape), then the tag stratum builds is keyed off the host's origin/main, while the tag stratum references is keyed off your local HEAD. For the two to match:

Contract: commit and push before applying. Your local HEAD must equal origin/main (i.e. pushed), or the built tag won't match the referenced tag and the container will fail to start on a missing image.

References

${var.<name>} is a normal string interpolation (see String interpolation). It works anywhere a ${...} placeholder is allowed — string attrs and string values inside maps and lists — and the resolved value is always a string:

command = "docker build -t app:${var.app_sha} ."
image   = "app:${var.app_sha}"
env     = { BUILD_TAG = "${var.app_sha}" }

The reference form is exactly two segments: var.<name>. There is no field access — ${var.app_sha.foo} is too short/long and errors. An unknown name errors with unknown var:

unknown var `nope` in reference `var.nope`

Like host and secret blocks, var bodies are literal-only: any reference inside a var body (default = host.h.addr, ${var.other}, etc.) errors with references not allowed inside \var` blocks`. Vars are leaves — they cannot depend on hosts, secrets, or each other.

Vars vs secrets

Both resolve a string from the environment at load time, but they are not interchangeable:

  • A var is plain config. Its value is printed in plan output and stored verbatim in state. Use it for non-sensitive values: image tags, build args, environment names.
  • A secret is confidential. Its value is redacted from CLI output and state behind a marker. Use it for passwords, tokens, keys. See Secrets.

There is no from_git_sha on secrets and no redaction on vars. Pick by sensitivity.

Errors

conditionerror
None of from_env / from_git_sha / default specifiedBadVarBody
No source yields a value and there is no defaultVarUnresolved
Any reference inside the var bodyRefInVarBlock
Unknown var name in a ${var.x} referenceUnknownVar
Duplicate name across -c filesDuplicateVar (names both paths)

See also