GitOps Branching Strategies for Kubernetes
Design effective Git repository structures and branching strategies for GitOps-driven Kubernetes deployments with Flux and Argo CD.
What you'll learn
- ✓Monorepo vs multi-repo layouts for GitOps
- ✓Branch-per-environment vs directory-per-environment
- ✓Promotion workflows across staging and production
- ✓Practical Flux and Argo CD configuration examples
- ✓How to handle secrets and environment-specific overrides
Prerequisites
None — this post is self-contained.
GitOps uses Git as the single source of truth for infrastructure and application configuration. A GitOps operator like Flux or Argo CD watches a repository and reconciles the cluster state to match what is in Git. The principle is simple, but the repository structure and branching strategy you choose determines whether your workflow scales cleanly or becomes a merge conflict nightmare.
Repository Layout Decisions
The first decision is how many repositories to use.
Monorepo
One repository contains all Kubernetes manifests for all services and environments:
infrastructure/
base/
api/
deployment.yaml
service.yaml
kustomization.yaml
worker/
deployment.yaml
service.yaml
kustomization.yaml
environments/
staging/
kustomization.yaml
patches/
production/
kustomization.yaml
patches/
Advantages: atomic changes across services, simple CI/CD, easy to search and refactor. Disadvantages: access control is harder (everyone can see production config), large diffs, and the blast radius of a bad merge is wider.
Multi-Repo
Separate repositories for application code and infrastructure config, or one repo per team:
# Repo: app-api (application code + Dockerfile)
# Repo: app-worker (application code + Dockerfile)
# Repo: infra-config (all Kubernetes manifests)
# or
# Repo: team-alpha-infra (manifests for team alpha's services)
Advantages: fine-grained access control, smaller blast radius, independent release cycles. Disadvantages: cross-service changes require coordinating multiple PRs, harder to maintain consistency.
Most teams start with a monorepo and split when access control or scale demands it.
Branch-Per-Environment vs Directory-Per-Environment
Branch-Per-Environment
Each Git branch represents an environment. The staging branch deploys to staging, the production branch deploys to production:
main ---------> staging cluster
production ---> production cluster
Promotion means merging or cherry-picking from staging to production. This seems intuitive but creates problems:
- Merge conflicts between branches that diverge over time
- No single view of all environments in one place
- Hard to compare staging and production configurations
- Cherry-picking is error-prone and tedious
Avoid this pattern. It works for small setups but breaks down quickly.
Directory-Per-Environment (Recommended)
Use a single branch (main) with directories representing environments. Kustomize overlays handle environment-specific differences:
base/
api/
deployment.yaml
service.yaml
kustomization.yaml
environments/
staging/
kustomization.yaml
production/
kustomization.yaml
The base contains the common configuration. Each environment directory contains a Kustomization that references the base and applies patches:
# environments/staging/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/api
patches:
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 2
images:
- name: myregistry/api
newTag: v1.5.0-rc.1
# environments/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/api
patches:
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 5
images:
- name: myregistry/api
newTag: v1.4.2
Promotion is changing the image tag in the production kustomization. One PR, one diff, one review.
Configuring Flux
Flux watches specific paths in a Git repository. Configure a Kustomization resource for each environment:
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: infra
namespace: flux-system
spec:
interval: 1m
url: https://github.com/myorg/infra-config
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: staging
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: infra
path: ./environments/staging
prune: true
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: api
namespace: default
Flux reconciles the ./environments/staging path every 5 minutes. When a PR merges that changes the staging image tag, Flux detects the change and rolls out the update.
Configuring Argo CD
Argo CD uses Application resources to map Git paths to clusters:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: staging-api
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/infra-config
targetRevision: main
path: environments/staging
destination:
server: https://staging-cluster.example.com
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
The selfHeal: true option automatically reverts manual changes to the cluster, ensuring Git remains the source of truth.
Promotion Workflows
Manual Promotion
A developer opens a PR that updates the image tag in the production kustomization. A reviewer approves it, the PR merges, and the GitOps operator deploys:
# Update production image tag
cd environments/production
kustomize edit set image myregistry/api:v1.5.0
git add .
git commit -m "promote api v1.5.0 to production"
git push origin main
Automated Promotion
Use Flux Image Automation to watch a container registry and update image tags in Git automatically:
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: api
namespace: flux-system
spec:
imageRepositoryRef:
name: api
policy:
semver:
range: ">=1.0.0"
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageUpdateAutomation
metadata:
name: infra
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: infra
git:
checkout:
ref:
branch: main
commit:
author:
email: flux@example.com
name: Flux
push:
branch: main
update:
path: ./environments/staging
strategy: Setters
Flux watches the registry, and when a new semver tag appears, it updates the image tag in the staging kustomization and commits the change. For production, keep promotion manual with a PR-based workflow.
Environment-Specific Overrides
Use Kustomize patches for differences beyond image tags:
# environments/production/patches/api-resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
template:
spec:
containers:
- name: api
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
env:
- name: LOG_LEVEL
value: "warn"
Reference the patch in the production kustomization:
# environments/production/kustomization.yaml
patches:
- path: patches/api-resources.yaml
This cleanly separates what is common (base) from what varies by environment (patches).
Wrap-up
Use a single branch with directory-per-environment layout. Avoid branch-per-environment unless you enjoy merge conflicts. Use Kustomize overlays for environment-specific configuration, automate image updates in staging, and keep production promotions behind a PR review. The repository structure you choose on day one will shape every deployment workflow that follows, so get it right early.
Related articles
- DevOps GitOps with ArgoCD: Deploy Kubernetes Apps Automatically
Learn GitOps principles and deploy Kubernetes applications with ArgoCD. Covers installation, app configuration, sync policies, multi-environment setups, and rollbacks.
- DevOps GitOps with ArgoCD for Kubernetes
A hands-on guide to implementing GitOps with ArgoCD: declarative deployments, sync policies, and multi-environment management.
- DevOps Container Orchestration: Docker Swarm vs Kubernetes
Compare Docker Swarm and Kubernetes for container orchestration. Learn the architecture, setup, scaling, and networking of both platforms with practical examples.
- DevOps Zero-Downtime Deployments: Strategies and Patterns
Master zero-downtime deployment strategies including rolling updates, blue-green, canary, and feature flags. Learn practical patterns for Kubernetes and cloud platforms.