In the last post, we successfully deployed our first application with GitOps. Now it’s time to take things a step further and handle a real-world scenario: managing multiple environments like development, staging, and production.
In any serious project, separating environments is essential for testing new features safely, validating them, and then promoting them to production. GitOps gives us a clean, automated, and auditable way to manage this process — all through Git commits.
Let’s dive into how to structure, configure, and automate multi-environment deployments using GitOps.
1. Why Manage Multiple Environments with GitOps?
In simple setups, deploying directly to production might seem fast. But as soon as teams grow or applications become critical, having isolated environments becomes essential.
In a traditional workflow, teams might manually update dev, staging, and prod clusters by hand or through scripts. Unfortunately, this manual process often leads to:
- Configuration drift, where environments slowly become inconsistent
- Human errors, especially under time pressure
- Untraceable changes, making troubleshooting difficult
GitOps offers a better solution. By treating environments as code — stored, versioned, and managed in Git — teams gain:
- Clear separation between environments
- Consistent promotion paths from dev to staging to production
- Auditability, where every change is traceable back to a Git commit
- Automation, reducing manual steps and the potential for mistakes
Instead of relying on memory or tradition, we make environments predictable and repeatable.
2. Structuring the Git Repository for Multiple Environments
The way you organize your Git repository plays a major role in successful multi-environment GitOps.
A clean structure could look like this:
your-repo/
└── clusters/
├── dev/
│ ├── apps/
│ └── kustomization.yaml
├── staging/
│ ├── apps/
│ └── kustomization.yaml
└── prod/
├── apps/
└── kustomization.yaml
Each environment — dev, staging, and prod — has its own directory. Inside each, you’ll define the applications, configurations, and resources specific to that environment.
This separation provides two big advantages:
- It allows independent updates to each environment without affecting others.
- It simplifies promotion — for example, by merging code from the dev folder into the staging folder when ready.
It’s worth noting that while the application itself might stay the same, each environment could have small differences — like the number of replicas, feature flags, or resource limits — all controlled in their respective directories.
3. Setting Up Kustomizations for Each Environment
Once we have our folder structure in Git, we need to teach Flux how to handle it. We do that using Kustomizations.
In GitOps, a Kustomization defines:
- What part of the repo to apply (e.g., dev apps)
- How often to check for changes
- Where in the cluster to apply them
Here’s an example Kustomization for the dev environment:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: dev
namespace: flux-system
spec:
interval: 5m0s
path: ./clusters/dev
prune: true
sourceRef:
kind: GitRepository
name: flux-system
We would create similar Kustomizations for staging and prod, adjusting the path and name accordingly.
By defining a Kustomization per environment, Flux will treat each one independently:
- Dev apps will deploy from the dev folder.
- Staging apps from the staging folder.
- Production apps from the prod folder.
Each environment can sync at its own pace and on its own terms.
4. Deployment Flow: From Dev to Production
Managing multiple environments also changes how deployments happen.
Instead of applying changes manually, the deployment process becomes a series of Git operations:
- Developers commit changes into the dev environment directory.
- Flux syncs those changes and deploys to the dev cluster automatically.
- After testing and validation, a pull request promotes the exact same configuration to the staging environment.
- After additional testing in staging, another pull request promotes it to production.
Each promotion step involves no cluster commands, just Git operations. This brings:
- Predictability: You can trust that production matches exactly what was tested.
- Traceability: Git history shows exactly what was promoted and when.
- Safety: No one needs direct cluster access to deploy or promote code.
This approach also encourages review culture: before something reaches production, it must pass through dev and staging, with peer reviews at each step.
5. What’s Next
Now that we have our environments separated and a promotion workflow mapped out, we’re almost ready for more powerful setups.
In the next post, we’ll explore how to use Helm charts with GitOps:
- Package applications using Helm
- Manage versions and configurations even more cleanly
- Combine Helm and Kustomize for flexible, scalable GitOps pipelines
GitOps is starting to feel real now — and you’re building serious, production-grade skills step-by-step.
Previous Posts in the Series
- Episode Zero — Why You Should Care and What to Expect
- Episode One — GitOps vs. Traditional CI/CD
- Episode Two — Setting Up a GitOps-Ready Kubernetes Cluster
- Episode Three — Deploying Your First App with GitOps
Enjoying this journey into GitOps?
Subscribe to get future episodes, real-world tutorials, and in-depth guides delivered right to your inbox!