A GitOps pipeline with approval gates
How to make Git the only way infrastructure changes — with a plan everyone can review before anything is applied.
GitOps means the Git repository is the source of truth: if a change is not merged, it does not reach production. The pipeline is what enforces that.
The stages
- Validate — on every push: formatting, syntax checks and linting (
terraform fmt -check,terraform validate,ansible-lint). - Plan — on every merge request: run
terraform planand the Ansible playbook in check mode, and attach the output to the merge request so reviewers see exactly what will change. - Approve — a named person approves the merge request. For production, require a second approver.
- Apply — after merge, apply the saved plan, not a fresh one, so what runs is what was reviewed.
- Verify — run tests against the result: reachability, routing tables, service health.
Design choices that save trouble
- Remote, locked state. Store Terraform state in a shared backend with locking, so two pipelines can never apply at once.
- Runners inside your network. A self-hosted runner that registers outbound over HTTPS can reach lab and production devices without opening any inbound firewall ports.
- Secrets out of the repository. Credentials live in the CI system's secret store or a vault, injected at run time.
- Separate environments. Lab and production are separate state files and separate approval rules, built from the same code.
If an engineer can still log in and change production by hand, the pipeline is advisory. Restrict direct access once the pipeline is trusted.