GitFlow combined with Terraform: maybe not the best choice.

Terrateam

On this page
GitFlow is a popular branching strategy that has served a lot of software teams well, especially using monorepos. The idea is to maintain long-lived branches like develop
and main
while isolating features, releases, and hotfixes in their own branches. This can help teams work on a repository without stepping on toes.
Here’s how it works:
Branches
main
→ Stable production branchdevelop
→ Integration branch for new featuresfeature/
→ New feature branches fromdevelop
release/
→ Prepares a version for productionhotfix/
→ Urgent fixes formain
bugfix/
→ Fixes applied before release
Workflow steps
- Start development → Branch from
develop
(feature/
) - Merge feature → Back into
develop
when done - Create a release → Branch from
develop
(release/
) - Finalize release → Merge into
main
and tag a version - Critical fixes? → Use
hotfix/
frommain
, then merge back
When it comes to infrastructure as code with Terraform or OpenTofu, GitFlow can create a lot of problems.
GitFlow doesn’t work well for Infrastructure as Code
For application development, GitFlow can make sense. Code is stateless, meaning multiple developers can work in parallel without worrying about underlying conflicts beyond merge conflicts. Once merged, branches can be deleted, and nothing breaks.
Infrastructure as code is different. Terraform does not just describe a set of files. It describes real, running infrastructure, and when multiple branches manipulate that infrastructure in parallel, things start to break.
GitFlow breaks down
State file conflicts
Terraform relies on state files to track live infrastructure. If two GitFlow branches modify the same resources, their state files will diverge. Merging those branches is not like merging text files. It is merging two different views of reality.
At best, Terraform detects conflicts and blocks you. At worst, a bad merge causes Terraform to delete or recreate resources unexpectedly. This can lead to downtime or other unintended consequences.
Long-lived branches drift
In GitFlow, feature branches can live for weeks before merging. In the world of Terraform, that is a long time. Infrastructure changes frequently. Resources can get added, deleted, or modified outside of Git, whether manually in the cloud console or by another branch merge.
By the time a long-lived branch finally merges, its plan may look drastically different than expected, resulting in failed applies or infrastructure drift.
Environment promotion don’t mix with git merges
GitFlow assumes that changes move from develop to release to main in a linear way. But infrastructure deployments are rarely that straightforward.
Sometimes you need to apply a change in staging first and verify it before rolling it out to production. Other times, hotfixes need to bypass staging entirely and go straight to production. Multi-region deployments often need to happen in parallel, not one after another.
GitFlow forces infrastructure into a process that does not match how teams actually deploy and promote environments.
Merging Terraform code is not straightforward
Merging application code is usually straightforward. Git reconciles differences in code, and tests confirm everything works.
With infrastructure, a successful merge does not always mean the final result is correct. Terraform will apply the most recent version of a resource, potentially overriding previous changes without warning.
GitFlow is complicated with CI/CD pipelines
GitFlow requires multiple steps for every change. Terraform plan runs on feature branches. Another apply happens when merging into develop. A separate plan runs on a release branch. Another apply happens when merging to main.
Multiply this by multiple environments like staging, production, and multiple regions, and the pipeline becomes a mess of conditional logic and custom workflows. That is a lot of overhead for something that does not match how infrastructure is actually deployed.
Separate environments by directories
GitFlow is not inherently bad, but it does not fit Terraform’s workflow. Instead, consider a simpler approach.
My preferred strategy is to separate environments by directories instead of branches. Instead of maintaining a staging branch, keep a dedicated directory for each environment. This makes it easier to update infrastructure without worrying about complex merge conflicts.
terraform/ ├── staging/ │ ├── main.tf │ ├── variables.tf ├── production/ ├── main.tf ├── variables.tf
Conclusion
GitFlow works for application code, but Terraform is different. When you try to force GitFlow onto live infrastructure, you introduce state conflicts, merge risks, and a lot of complexity. Instead of relying on long-lived branches and complex merges, use short-lived feature branches, keep environment configurations separate, and automate your workflows. The best Terraform processes are the ones that keep things simple and predictable.
While GitFlow can introduce complexity for Terraform, some teams still find ways to make it work. At Terrateam, we support GitFlow and other branching strategies, so teams can choose what works best for them.