Core concepts
The mental model — Stacks, Runs and their phases, execution modes, approvals pinned to plans, the two locks, dependencies, runner tiers, and what gets metered.
Stacks
A Stack is the unit you create and configure: one Git source
(repository + path + branch), one OpenTofu version from the
curated catalog, one state.
Organizations own Stacks flat — there’s no grouping layer to administer. If
you’re used to other tools: a Stack is not a tofu workspace; we don’t
overload that word.
Runs and phases
A Run is one execution against a Stack — a plan phase, then (for changes) an apply phase. One resource with phases means approvals, audit, and the UI all point at the same thing. Three kinds:
- Speculative — plan-only, the PR-check experience. Never applies, never locks, and never blocks the apply queue. A new push to a pull request supersedes that PR’s older speculative Runs — the check always reflects the newest head, and dead heads stop costing runner minutes.
- Change — plans, then applies; triggered by a push to the tracked branch or manually.
- Destroy — a Change whose plan tears everything down. Manual-only, and it always requires approval, whatever the execution mode.
At most one apply-capable Run is active per Stack; the rest queue in order. Speculative Runs run beside the apply queue under their own concurrency bounds — a busy repository queues excess plans instead of fanning out unbounded compute.
Execution modes
A Stack’s execution mode decides how a change applies relative to merging
its pull request — apply from the branch before merge, apply on merge with a
confirmation, apply automatically, or run only on demand. Every mode still gets
a plan check on the pull request; only the apply trigger differs. The default,
merge_then_apply, parks every change at an approval. See
Execution modes.
Approvals are pinned to plans
An approval binds to one specific plan artifact — not a branch, not a Run in the abstract. The apply executes the exact saved plan; if the plan is ever re-created, existing approvals are void. What you read is what runs.
The two locks
The run queue serializes hosted Runs; the backend lock — taken through
the standard LOCK/UNLOCK verbs — serializes every writer, including
someone running tofu apply from a laptop. They layer; neither replaces
the other.
Variables
Per-Stack inputs to Runs: OpenTofu input variables (TF_VAR_…) or plain
environment variables, one record with a kind. Marking a variable
sensitive makes it write-only — encrypted at rest, never readable back
through any API; update means overwrite. Log masking of sensitive values is
best-effort, not a boundary.
Dependencies and cascade
A dependency wires one Stack to another within your organization: when an upstream applies, its dependents can run (each gated by its own execution mode), and a dependent can read the upstream’s root outputs — never its full state. The graph stays acyclic, and a pull request shows the blast radius before you apply. See Dependencies and cascade.
Runner tiers and metering
Hosted Runs execute in a sandbox sized by the Stack’s runner tier —
small, medium, or large, a curated ladder of CPU, memory, disk, and run
timeout (the exact envelopes). The only thing
metered is runner minutes, per tier. Stacks, users, state storage, and state
operations are free.
Run identity
Every Run can present a short-lived OIDC token asserting exactly which Stack and which phase is executing — see Run identity for wiring it to your cloud.