Stacks
A stack is one unit of OpenTofu state. It is the thing you deploy, and the anchor for its runs, logs, and credentials. You create stacks under an organization, in the UI or via the CLI.
What a stack points at
| Field | What it does |
|---|---|
| Repository URL | Where the OpenTofu config lives. |
| Branch | The branch GantryCD tracks. A push here triggers a plan. |
| Working directory | The subdirectory within the repo to run in. Root means the whole repo. |
| OpenTofu version | Pinned per stack; the runner installs it on demand. |
| Labels | Free-form metadata, plus the runner selector (below). |
| Managed backend | When on, GantryCD provisions and injects the state backend for you — and can administer the state directly (see State administration). When off, you bring your own backend in your OpenTofu config. |
A stack must be synced at least once — see Connecting repositories — before it can be deployed, so GantryCD knows which commit to run.
Labels and choosing runners
Labels are key=value pairs. Most are free-form metadata you can filter on. Keys
under the reserved gantrycd:runner-group: prefix are special: they form the
stack’s runner selector, which decides where runs execute.
A run is assignable to a runner group only when every pair in the selector is present on that group’s labels — exactly like Kubernetes node selectors. A stack with no selector labels falls back to the organization’s default runner group.
Set selector labels to pin a stack to a region, a network, or a specific pool:
gantrycd:runner-group:region = eu-west
gantrycd:runner-group:network = private
See Runner groups for the other side of this match.
Tuning the runner pod (Kubernetes)
Where the selector chooses which group runs a stack, keys under the reserved
gantrycd:runner-spec: prefix tune the pod that group launches for this stack’s
runs — currently the Kubernetes
runner’s image and CPU/memory:
gantrycd:runner-spec:kubernetes_request_cpu = 1
gantrycd:runner-spec:kubernetes_limit_cpu = 2
gantrycd:runner-spec:kubernetes_request_memory = 4Gi
gantrycd:runner-spec:kubernetes_limit_memory = 8Gi
gantrycd:runner-spec:kubernetes_image = ghcr.io/me/runner:v2
Any key you omit falls back to the group’s configured default. Image overrides are operator-gated (the runner pod runs under the group’s credentials); CPU/memory apply by default. Reach for these to size individual stacks within one pool — for a different image or fleet by default, use a separate runner group instead. Details and the security note are on the Kubernetes runner group page.
Bulk actions from the stacks list
The stacks list lets you act on many stacks at once — the natural follow-on to filtering. Narrow the list (by name, repository, branch, label, or project root), then tick the stacks you want, or use the header checkbox to select all that match the current filter. Shift-click extends a range.
A toolbar then offers the everyday actions across the whole selection:
- Sync — pull each selected stack’s latest tracked commit from the SCM.
- Run — trigger a plan (or refresh, from the dropdown) on each. There is deliberately no bulk destroy; that stays a single-stack action.
- Skip all — discard every queued and pending deployment on each selected stack (the bulk form of the per-stack Skip all queued), to clear a backlog quickly. To drop only some of one stack’s queued deployments, tick them individually on that stack’s Deployments tab instead — see Clearing queued work.
Each action is permission-aware: it only applies to the stacks you may act on
(Sync needs update, Run needs deploy, Skip all needs confirm),
and a confirmation step shows exactly how many will run and how many are
skipped — for lack of permission, or, for a run, because the stack isn’t synced
yet. Unsynced stacks can be synced first, then run with one checkbox.
The actions fan out a few at a time and report per-stack success or failure as they go; triggered runs then queue and advance per stack exactly as a single-stack trigger does (see Deployments & runs).
Environment variables
A stack can carry environment variables, including secret ones, that are
injected into every run’s OpenTofu process — perfect for TF_VAR_* inputs or
provider configuration. Secret values are encrypted at rest and write-only:
once set, they’re never returned to the UI or API.
These variables sit below the credentials GantryCD mints (state, cloud), so a stack variable can never shadow a managed credential.
Contexts: shared variable bundles
Repeating the same variables across stacks gets old. A context is an org-scoped, reusable bundle of environment variables and secrets that any number of stacks can reference.
A stack picks up a context two ways: by referencing it explicitly (an ordered list on the stack’s settings page), or by auto-attach — a context whose label selector matches the stack (below). At deploy time GantryCD merges every context that applies, then overlays the stack’s own variables. From lowest to highest precedence:
- Auto-attached contexts — the broad org defaults, applied in
auto_attach_priorityorder (a higher priority wins on a conflicting key). - Explicitly-referenced contexts — in list order, later wins; each overrides any auto-attached context.
- The stack’s own variables — always win.
So a shared-aws context can set a default region for ten stacks, an explicit
context can override it for one of them, and that stack can still override it
locally. Manage contexts under Org settings → Contexts, and attach them on
the stack’s settings page.
A stack resolves to at most 20 contexts (explicit plus auto-attached combined); past that the deploy fails with a clear error rather than silently dropping a context.
Auto-attach by label selector
A context can carry an auto-attach selector — a set of key=value
labels — plus an auto-attach priority. When
the selector is non-empty, the context implicitly attaches to every stack
whose labels are a superset of the selector, on top of any stack that names
it explicitly. An empty selector disables auto-attach.
For example, a context with the selector team=payments attaches to every stack
labelled team=payments (whatever else the stack is labelled) — no per-stack
wiring. Give a new stack that label and it inherits the context’s variables,
secrets, and hooks automatically.
Selectors match your free-form labels only; the reserved gantrycd: label
keys (runner selection and pod-spec) can’t be selected on. A context that both
auto-matches a stack and is referenced by it explicitly counts once, at its
explicit position.
Auto-attach can pull secrets onto a stack, so who may set the matching label matters. An org can mark label keys as protected: once a key is protected, changing it on a stack needs a dedicated per-key permission, so only the right people can make a stack match — and inherit — a context’s secrets. See Protected labels.
State administration
When a run crashes mid-apply it can leave the OpenTofu state locked, blocking every later plan and apply, or leave the state itself in a bad shape. Three advanced, separately-granted operations — found under Manage Stack → State for an existing managed stack — let a trusted operator recover. They are not shown while creating a stack because there is no state to recover yet:
- Manage State Lock — preview who holds the lock and force-unlock a stale
one. Gated by the State Lock Manager role (
unlock_state). - Overwrite State — upload a known-good
terraform.tfstateto replace the current one (the previous state is backed up first). Gated by the State Overwriter role (overwrite_state). Found on the Upload file tab of the Overwrite / Restore State dialog. - Restore version — roll the state back to a stored prior version instead of
uploading one. The list, with Load more paging, and a per-row Restore
live on the Restore version tab of the same dialog. Gated by the State
Restorer role (
restore_state). Requires S3 object versioning enabled on the state bucket; when it isn’t, the tab shows a prompt to enable it.
Each is a distinct permission, separate from deploying or running commands, so you can hand out state recovery without handing out everything. All refuse while a deployment is in flight — cancel it first.
Managed backends only. GantryCD performs these directly on the state it provisions, so they are available only when Managed backend is on. For a bring-your-own backend GantryCD doesn’t know where the state lives and cannot force-unlock or push state — run
tofu force-unlock/tofu state pushyourself via a Run Command instead, where the runner initializes your backend. See State administration for details.
Lifecycle notes
- Renaming or repointing a stack is allowed. Its immutable storage namespace is preserved, so logs and state keys stay stable across a rename.
- Deleting a stack cascades its runs, deployments, and PR rows.
- A stack holds at most one state-changing deployment at a time — see Deployments.