teriac server
Self-hosted pull request automation for Terraform, Terragrunt and OpenTofu: what happens on every push, how a change goes from plan to apply, and what you need to run it.
teriac server is pull request automation for Terraform, Terragrunt and OpenTofu that you host yourself, with Docker or Helm.
On every push it does three things:
- Finds the affected projects, with no config needed.
- Plans each one with the exact tool version it pins.
- Posts the plan as a new comment, showing the exact command to apply each project, plus a check run per project with an Apply button.
Every action goes through per-project permissions and required approvers taken from your Git platform's users and teams. Scheduled drift checks post to Slack, Teams, Discord, Google Chat or any webhook, and tag who last changed each resource.
The life of a pull request
push to a PR
│
▼
GitHub webhook ──▶ teriac server (/events/github)
│
├─ repo on the allowlist? no → ignored
├─ find affected projects modules, Terragrunt includes, teriac:watch
├─ lock each project + workspace another PR holds it → 🔒 noted in the comment
├─ plan each one exact pinned version, saved plan file
├─ policy checks audit or enforce
└─ comment + one check run per project, with an Apply button
│
▼
review, approvals
│
▼
"teriac apply -p live/prod/vpc" or the Apply button
│
├─ permission? apply / apply_destroy for this project
├─ approvals? of the current commit, author's own never counts
├─ requirements? mergeable, undiverged, two-person rule, policy
├─ plan current? no newer commit, no dependency applied since
└─ apply the saved plan ──▶ apply report, audit log
│
▼
merge or close ──▶ locks released, plans and checkouts deletedKey ideas
Apply means the reviewed plan. teriac saves each plan on its own disk. An apply always runs that saved plan, so what runs is exactly what was reviewed. A newer push replaces it.
Locks per project. When a PR plans a project, it takes that project's lock (per workspace). Another PR that touches the same project is told who holds it, until the first PR is applied, unlocked or closed.
Rules live on the server. Permissions, approvals and policies are in repos.yaml on the server, so
a pull request can't change its own rules. A repo's teriac.yaml can only make them stricter.
The record can't be edited. Check runs and the dashboard are the trusted record; teriac never reads its own comments back, and warns when someone edits them.
What you need
| A GitHub App | For webhooks, comments and check runs. GitHub.com or GitHub Enterprise Server |
| A place to run it | Docker, or Kubernetes 1.27+ with the Helm chart |
| A volume | For checkouts, saved plans, tool binaries, the provider cache and the SQLite database. 20 GiB is a good start |
| Cloud credentials | Whatever your Terraform needs: env vars, workload identity (IRSA, GKE, Azure) or mounted files |
| A public URL | For GitHub's webhooks. The dashboard can stay internal |
No database server, queue or Redis: teriac keeps its state in SQLite on the volume and runs as one replica. Turn on backups to copy that database to S3, GCS or Azure on a schedule; a new, empty volume is restored from the newest one.
Next
- Quick start: GitHub App, Docker or Helm, first PR
- PR commands: plan, apply, unlock and Terraform commands
- Rules and permissions:
repos.yaml, approvals, per-command permissions - What gets planned: project detection,
teriac:watch, Terragrunt dependencies - Policy checks and safety: policy rules, the two-person rule, tamper-proof output
- Drift checks: scheduled checks with chat alerts
- Risk and guardrails: risk scores, change freezes, protected resources, secret scan
- Emergency access and reports: break-glass grants, weekly summary, delivery metrics
- Dashboard and API: runs, locks, drift, the Guardrails page and the API
- Backups: scheduled database backups to S3, GCS or Azure, restored on start
- Helm chart and Settings
- Guide: EKS end to end: IRSA, an ALB with HTTPS, Okta, rules and drift alerts
- Security model