Docs / Make changes safely
Delivery pipelines
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.
Planned Committed, but not built yet. Everything below describes the planned design.
TractorStack is planned to run CI/CD pipelines for infrastructure changes on a self-hosted GitLab inside your network, not on a service we host. A change starts as a merge request, the pipeline shows exactly what it would do, a named person approves it, and only then does anything touch a real machine. The same steps run for a firewall rule, a new VM on Proxmox VE or a Compose stack update.
Self-hosted GitLab and runners on site
TractorStack is built around GitLab: merge requests for proposing changes, GitLab merge request approvals for sign-off, .gitlab-ci.yml for the pipeline, and GitLab Runners installed on your own machines. Other Git servers could work, but GitLab is what the design targets and what we test against. The jobs that talk to hypervisors and servers run on the site runner, so credentials for Proxmox VE, vSphere or SSH stay on site.
On an offline or air-gapped network the pipeline still works, provided the runner can reach the local mirrors described in Offline and air-gapped.
CI/CD pipelines for infrastructure changes: the stages
- Lint and validate.
tofu fmt -checkandtofu validate,ansible-lint,docker compose configfor Compose files, and a YAML check for Helm values. Cheap and fast, so mistakes fail in seconds. - Plan and check.
tofu plansaved to a file, andansible-playbook --check --diffagainst the target hosts. The output is attached to the merge request so reviewers read the real difference, not just the code diff. - Guardrail checks. The saved plan goes through guardrail checks. A blocking rule stops the pipeline here; a warning is shown to the reviewer.
- Approval. A person with the right role approves the merge request. Pipelines and automation tokens can start runs but cannot approve them. See change requests and sign-off.
- Apply. The approved plan file is applied, not a fresh plan. If the infrastructure moved since the plan was made, the apply stops and asks for a new plan.
- Post-apply checks. Health endpoints answer, services are up, a quick
tofu planshows no remaining difference, and the result goes into the audit trail.
A planned outline
An illustrative sketch in the shape of a .gitlab-ci.yml, not a file from a release. Job names and images will change.
# planned example: .gitlab-ci.yml outline
stages: [validate, plan, guardrails, apply, verify]
validate:
stage: validate
script:
- tofu fmt -check
- tofu validate
- ansible-lint
plan:
stage: plan
script:
- tofu plan -out=change.plan
- tofu show -json change.plan > plan.json
artifacts:
paths: [change.plan, plan.json]
guardrails:
stage: guardrails
script:
- conftest test plan.json
apply:
stage: apply
tags: [site-runner]
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- tofu apply change.plan
verify:
stage: verify
script:
- tofu plan -detailed-exitcode
Why the plan file matters
Reviewers approve what the plan says will happen. Applying a new plan after approval would mean applying something nobody looked at. Keeping the saved plan as the thing that gets applied closes that gap, and the plan is kept with the run record. State for these plans lives in PostgreSQL on site, as described in where state lives.
For changes that go to many sites at once, the same pipeline is planned to run in rollout waves instead of everywhere at the same moment. On Kubernetes from stage 3, application deployments follow Git as the source of truth with Argo CD.
Last updated 25 October 2026 · Questions or corrections: sales@infratractor.com