Docs / Make changes safely
Where state lives
Planned design. TractorStack is in development. This page describes how it is designed to work; nothing here is available to install yet. See the roadmap for the status of each part.
In design Being designed now, alongside stage 1. Everything below is the planned design.
OpenTofu keeps a record of everything it has created: which VM has which ID, which disk belongs where, which network a machine is on. That record is the state, and losing it or letting two runs edit it at once causes real damage. TractorStack plans to use the OpenTofu state backend in PostgreSQL that runs on your own site, encrypted before it is written, so state is covered by the same data residency and backups as the rest of your data.
OpenTofu state backend in PostgreSQL
OpenTofu has a built-in pg backend. It stores state in a table inside a PostgreSQL schema and uses PostgreSQL advisory locks so that only one run can change it at a time. No extra service is needed: the database is already part of every stage, from stage 1 onwards.
The design gives each environment its own schema, named after it, for example tofu_plant_a_prod, and a database user that can reach only that schema. The site runner holds that user’s password; people normally don’t need it at all.
This works the same whatever sits underneath. Proxmox VE is the primary target, but if you run vSphere, another hypervisor or bare metal, the state still records what OpenTofu manages there and still lives in your PostgreSQL.
Encryption
State can contain IP addresses, disk paths and sometimes passwords a provider returned. Since version 1.7, OpenTofu can encrypt state and plan files on the client before they are written. The design turns this on from the first apply, with a key that is held as a secret on site and never stored in the repository.
# Planned example only
terraform {
backend "pg" {
schema_name = "tofu_plant_a_prod"
}
encryption {
key_provider "pbkdf2" "site" {
passphrase = var.state_passphrase
}
method "aes_gcm" "site" {
keys = key_provider.pbkdf2.site
}
state { method = method.aes_gcm.site }
plan { method = method.aes_gcm.site }
}
}
A database dump then holds ciphertext, not your network map. If the passphrase is lost, the state can’t be read, so it belongs with your other recovery secrets in secrets and credentials.
Locking
If two pipelines plan or apply against the same environment, the second waits or fails on the lock rather than both writing. When a job is killed halfway, a lock can stay behind. The planned runbook covers checking that no run is actually active before releasing it with the OpenTofu command for stale locks, and recording that in the merge request.
Backups of state
Because state is a table in PostgreSQL, it is included in the nightly pgBackRest backups to RustFS and in the automatic test restore described in backups and restore tests. The design adds two small extras:
- After each apply, the pipeline is planned to export a copy of the encrypted state as a job artifact kept for a limited time, so a bad apply can be compared with the version before it.
- The restore test checks that the restored state decrypts and that
tofu planagainst it reports no unexpected changes.
Where the site runs PostgreSQL high availability with Patroni Built, state benefits from automatic failover like any other database. Patroni manages leader election across several PostgreSQL nodes, and the runner reaches the current primary through a routing layer such as HAProxy or a virtual IP. Details of our reference layout will be published.
Before PostgreSQL exists
The first run on a new site has nothing to store state in, because it is the run that creates the database VM. The design starts with encrypted local state on the runner, then moves it into PostgreSQL with tofu init -migrate-state once the database is up. See OpenTofu for the rest of how it is used.
Last updated 25 October 2026 · Questions or corrections: sales@infratractor.com