Module 3.2: Repository Strategies
Discipline Module | Complexity:
[MEDIUM]| Time: 30-35 min
Prerequisites
Section titled “Prerequisites”Before starting this module:
- Required: Module 3.1: What is GitOps? — GitOps fundamentals
- Required: Git branching and PR workflow experience
- Recommended: Experience managing multiple services/environments
What You’ll Be Able to Do
Section titled “What You’ll Be Able to Do”After completing this module, you will be able to:
- Design repository structures that support GitOps at scale — monorepo, multi-repo, or hybrid approaches
- Implement branch and directory strategies that map cleanly to environments and team ownership
- Evaluate repository access patterns to prevent configuration drift and unauthorized changes
- Build repository conventions that make GitOps workflows self-documenting and auditable
Why This Module Matters
Section titled “Why This Module Matters”You’ve decided to adopt GitOps. Great! Now where do you put things?
This seemingly simple question determines:
- Who can change what: Access control
- How fast you can deploy: Sync times, blast radius
- How teams collaborate: Coupling vs autonomy
- How you recover from disasters: What to restore from where
Bad repository structure creates friction. Good structure enables flow.
This module helps you choose the right structure for your organization.
The Core Decision: Monorepo vs Polyrepo
Section titled “The Core Decision: Monorepo vs Polyrepo”Monorepo
Section titled “Monorepo”All configuration in a single repository.
gitops-config/├── apps/│ ├── frontend/│ │ ├── base/│ │ └── overlays/│ │ ├── dev/│ │ ├── staging/│ │ └── prod/│ ├── backend/│ │ ├── base/│ │ └── overlays/│ └── database/├── infrastructure/│ ├── cert-manager/│ ├── ingress-nginx/│ └── monitoring/└── clusters/ ├── dev/ ├── staging/ └── prod/Pros:
- Single place to see everything
- Easier cross-cutting changes
- Simplified tooling
- Single set of access controls
Cons:
- Can become large and slow
- Tight coupling between teams
- Harder to scale permissions
- One repo’s issues affect everyone
Best for:
- Smaller organizations (< 50 engineers)
- Platform teams managing infrastructure
- Strong central control needed
Polyrepo
Section titled “Polyrepo”Configuration spread across multiple repositories.
# Repository: team-a-configteam-a-config/├── service-1/│ └── overlays/└── service-2/ └── overlays/
# Repository: team-b-configteam-b-config/├── api-gateway/└── auth-service/
# Repository: platform-configplatform-config/├── infrastructure/├── policies/└── cluster-addons/Pros:
- Team autonomy
- Fine-grained access control
- Independent scaling
- Smaller, focused repos
Cons:
- Harder to see full picture
- Cross-cutting changes need multiple PRs
- More tooling complexity
- Potential for drift between repos
Best for:
- Larger organizations
- Strong team boundaries
- High autonomy cultures
Hybrid Approach
Section titled “Hybrid Approach”Most organizations land somewhere in between:
# Platform repo (central team)platform-config/├── infrastructure/├── policies/└── base-configs/
# Team repos (each team owns)team-a-apps/├── service-1/└── service-2/
team-b-apps/├── api/└── worker/Platform repo: Shared infrastructure, policies, base configurations Team repos: Team-specific applications and customizations
App Repo vs Config Repo
Section titled “App Repo vs Config Repo”Another key decision: where does configuration live relative to application code?
Single Repo (App + Config Together)
Section titled “Single Repo (App + Config Together)”my-service/├── src/│ └── main.go├── Dockerfile├── Makefile└── deploy/ ├── base/ │ ├── deployment.yaml │ ├── service.yaml │ └── kustomization.yaml └── overlays/ ├── dev/ ├── staging/ └── prod/Pros:
- Everything in one place
- App and config versioned together
- Simpler for small teams
- Natural code review includes deploy changes
Cons:
- App changes trigger config pipeline
- Config changes trigger app pipeline
- Different audiences (dev vs ops)
- CI/CD complexity
Separate Repos
Section titled “Separate Repos”# Repository: my-service (app code)my-service/├── src/├── Dockerfile└── Makefile
# Repository: my-service-config (GitOps config)my-service-config/├── base/│ ├── deployment.yaml│ └── service.yaml└── overlays/ ├── dev/ ├── staging/ └── prod/Pros:
- Clean separation of concerns
- Different permissions (devs vs ops)
- Config changes don’t rebuild app
- Clear GitOps boundary
Cons:
- Two repos to manage
- Coordination needed
- More complex CI/CD
The Common Pattern
Section titled “The Common Pattern”Most teams settle on:
┌─────────────────────────────────────────────────────────────┐│ Application Repo ││ ││ Source code + Dockerfile + CI pipeline ││ CI builds image, pushes to registry ││ CI updates image tag in Config Repo │└───────────────────────────┬─────────────────────────────────┘ │ Updates image tag │ ▼┌─────────────────────────────────────────────────────────────┐│ Config Repo ││ ││ Kubernetes manifests, Kustomize overlays ││ GitOps agent watches this repo ││ Agent syncs to cluster │└─────────────────────────────────────────────────────────────┘Try This: Map Your Current Structure
Section titled “Try This: Map Your Current Structure”Draw your current repository structure:
Current structure:├── Where is app code? _________________├── Where are Kubernetes manifests? _________________├── Where is infrastructure config? _________________├── How many repos total? _________________
Questions:1. Can a team deploy without touching other teams' repos? Y/N2. Can you see all deployed services in one place? Y/N3. Do app changes require config changes in separate commits? Y/N4. Are permissions appropriately scoped? Y/NDirectory Structures
Section titled “Directory Structures”How you organize files within repos matters as much as which repos you use.
Environment-Based Structure
Section titled “Environment-Based Structure”my-service/├── base/│ ├── deployment.yaml│ ├── service.yaml│ ├── configmap.yaml│ └── kustomization.yaml└── overlays/ ├── dev/ │ ├── kustomization.yaml │ ├── replica-patch.yaml │ └── config-patch.yaml ├── staging/ │ ├── kustomization.yaml │ └── replica-patch.yaml └── prod/ ├── kustomization.yaml ├── replica-patch.yaml ├── hpa.yaml └── pdb.yamlHow Kustomize works:
apiVersion: kustomize.config.k8s.io/v1beta1kind: Kustomizationresources: - deployment.yaml - service.yaml - configmap.yaml
# overlays/prod/kustomization.yamlapiVersion: kustomize.config.k8s.io/v1beta1kind: Kustomizationresources: - ../../basepatches: - replica-patch.yaml - hpa.yaml - pdb.yamlCluster-Based Structure
Section titled “Cluster-Based Structure”When managing multiple clusters:
clusters/├── dev-cluster/│ ├── kustomization.yaml│ ├── apps/│ │ ├── frontend/│ │ └── backend/│ └── infrastructure/│ ├── cert-manager/│ └── ingress/├── staging-cluster/│ └── ...└── prod-cluster/ ├── us-east/ │ └── ... └── eu-west/ └── ...Application-Centric Structure
Section titled “Application-Centric Structure”Organized by application, then environment:
apps/├── frontend/│ ├── base/│ └── overlays/│ ├── dev/│ ├── staging/│ └── prod/├── backend/│ ├── base/│ └── overlays/├── worker/│ └── ...└── infrastructure/ ├── monitoring/ ├── logging/ └── ingress/Choosing a Structure
Section titled “Choosing a Structure”| Factor | Environment-Based | Cluster-Based | App-Centric |
|---|---|---|---|
| ”What’s in prod?” | Check each app’s prod/ | Check prod-cluster/ | Check each app’s prod/ |
| “What apps in this cluster?” | Check multiple places | One place | Check multiple places |
| ”All configs for this app?” | One place | Multiple clusters | One place |
| Best for | Single cluster per env | Multi-region/cluster | App-focused teams |
Did You Know?
Section titled “Did You Know?”-
Google’s entire codebase lives in one repository with billions of files. They built custom tooling (Piper) to make this work. Most organizations can’t replicate this.
-
The “trunk-based development” pattern pairs well with directory-per-environment GitOps. Short-lived branches for changes, merge to main, promote through directories.
-
Spotify pioneered many polyrepo patterns for autonomy, but found they needed strong conventions and tooling to avoid chaos. Their “Golden Path” concept addresses this.
-
Microsoft’s Windows codebase moved to Git in 2017 with over 3.5 million files—they had to create Git Virtual File System (GVFS, now VFS for Git) to make it practical. Repository strategy isn’t just about organization, it’s about tooling limitations.
War Story: The Branch Strategy That Backfired
Section titled “War Story: The Branch Strategy That Backfired”A company I worked with tried branch-per-environment GitOps:
The Setup:
main branch → productionstaging branch → stagingdevelop branch → developmentThe Theory:
- Merge develop → staging to promote
- Merge staging → main to go to prod
- Each environment has “its own branch”
What Actually Happened:
Week 1: “This is clean!”
Week 4:
develop: 127 commits ahead of stagingstaging: 43 commits ahead of mainWeek 8:
- Merging staging → main caused massive conflicts
- Some features accidentally skipped staging
- “Which branch has the fix?” became common
- Cherry-picks created divergence
- Team spent hours resolving merge conflicts
The Root Problem:
Branches diverge. That’s what branches do. Environment promotion isn’t the same as code development.
The Fix:
Switched to directory-per-environment:
config-repo/└── my-service/ ├── base/ └── overlays/ ├── dev/ ← always in main ├── staging/ ← always in main └── prod/ ← always in mainPromotion became:
# Copy dev image tag to stagingyq eval '.images[0].newTag = "v1.2.3"' -i overlays/staging/kustomization.yaml
# PR, review, merge# GitOps agent syncs stagingResults:
- No merge conflicts
- Clear promotion path
- All envs visible in one branch
- PR shows exactly what changes
Lesson: Branches are for code development, not environment promotion.
Branch Strategies (And Why to Avoid Them)
Section titled “Branch Strategies (And Why to Avoid Them)”The Temptation
Section titled “The Temptation”Branches feel natural for environments:
- “main is prod”
- “develop is dev”
- “we control promotion with merges”
The Problems
Section titled “The Problems”Problem 1: Divergence
Branches naturally diverge. Environments shouldn't.
develop: adds feature A, B, Cstaging: only has A, B (C not promoted)main: only has A (B pending)
Now you have three different states to reason about.Problem 2: Merge Conflicts
Config conflicts are worse than code conflicts.Code: "which version of this function?"Config: "which version of the cluster state?"
Bad merges → bad deployments.Problem 3: Lost Changes
Feature in develop, cherry-picked to main,but staging branch never got it.
"Why is this bug in staging but not prod?"Problem 4: Audit Complexity
Q: "What changed between staging and prod?"A: "Let me diff two branches that have diverged significantly..."
vs. Directory-based:A: "Let me diff two directories on main."When Branches Might Work
Section titled “When Branches Might Work”- Very simple setups (2 environments max)
- Full lockstep promotion (always all changes)
- Strong discipline and tooling
- Small teams with clear ownership
The Recommendation
Section titled “The Recommendation”Use directories, not branches, for environment separation.
# Instead of:git checkout staginggit merge develop
# Do:# Update prod/kustomization.yaml with new image taggit commit -m "Promote v1.2.3 to prod"git push origin mainCommon Mistakes
Section titled “Common Mistakes”| Mistake | Problem | Solution |
|---|---|---|
| Branch-per-environment | Divergence, merge hell | Directory-per-environment |
| Giant monorepo without tooling | Slow, coupled | Split or invest in tooling |
| Too many small repos | Fragmentation, no visibility | Consolidate by team/domain |
| Mixing app code and config | Wrong triggers, permissions | Separate config repo |
| No clear ownership | Confusion, drift | Document who owns what |
| Inconsistent structure | Hard to automate | Standardize patterns |
Quiz: Check Your Understanding
Section titled “Quiz: Check Your Understanding”Question 1
Section titled “Question 1”Why is branch-per-environment problematic for GitOps?
Show Answer
Several reasons:
-
Branches diverge: Unlike code, environment configs shouldn’t diverge from each other except for intentional differences (replicas, resources)
-
Merge conflicts: Config merge conflicts can cause deployment failures
-
Lost changes: Cherry-picks and selective merges can miss changes
-
Audit difficulty: Comparing branches that have diverged is harder than comparing directories
-
Cognitive load: “Which branch is truth for which env?”
Better approach: Directory-per-environment on a single branch (main). Promotion = update the directory, PR, merge.
Question 2
Section titled “Question 2”When would you choose polyrepo over monorepo?
Show Answer
Choose polyrepo when:
- Large organization: Many teams, need independence
- Strong team boundaries: Teams shouldn’t see/change each other’s configs
- Different access needs: Varying permission requirements
- Scale concerns: Single repo would be too large
- Autonomy culture: Teams want full ownership
Choose monorepo when:
- Small organization: < 50 engineers typically
- Central platform team: Manages most config
- Cross-cutting changes: Frequent changes affecting multiple services
- Visibility: Need to see everything in one place
Most organizations use hybrid: platform monorepo + team polyrepos.
Question 3
Section titled “Question 3”You have 10 microservices and 3 environments (dev, staging, prod). How would you structure the config repo(s)?
Show Answer
Several valid approaches:
Option A: Single repo, app-centric
config/├── services/│ ├── service-1/│ │ ├── base/│ │ └── overlays/{dev,staging,prod}│ ├── service-2/│ │ ├── base/│ │ └── overlays/{dev,staging,prod}│ └── ... (10 services)└── infrastructure/ └── overlays/{dev,staging,prod}Option B: Single repo, env-centric
config/├── dev/│ ├── services/│ └── infrastructure/├── staging/│ └── ...└── prod/ └── ...Option C: Team repos (if teams own different services)
team-a-config/ (services 1-4)team-b-config/ (services 5-7)team-c-config/ (services 8-10)platform-config/ (infrastructure)Recommendation: Option A is most common for 10 services.
- Base reduces duplication
- Overlays capture env differences
- One place per service
Question 4
Section titled “Question 4”How does CI interact with GitOps config repos?
Show Answer
Typical flow:
1. Developer pushes code to app repo2. CI builds and tests3. CI builds container image4. CI pushes image to registry (e.g., myapp:v1.2.3)5. CI updates config repo: - Clone config repo - Update image tag in dev overlay - Commit and push (or create PR)6. GitOps agent detects config repo change7. Agent syncs new image to clusterKey points:
- CI doesn’t deploy directly to cluster
- CI only updates Git (image tags)
- GitOps agent does actual deployment
- Image tag is the link between CI and GitOps
Example CI step (GitHub Actions):
- name: Update config repo run: | git clone https://github.com/org/config-repo cd config-repo yq eval '.images[0].newTag = "${{ env.IMAGE_TAG }}"' \ -i services/myapp/overlays/dev/kustomization.yaml git commit -am "Update myapp to ${{ env.IMAGE_TAG }}" git pushHands-On Exercise: Design Your Repository Structure
Section titled “Hands-On Exercise: Design Your Repository Structure”Design a GitOps repository structure for a scenario.
Scenario
Section titled “Scenario”Your organization has:
- 3 teams (Platform, Frontend, Backend)
- 8 services total:
- Platform: cert-manager, ingress-nginx, monitoring
- Frontend: web-app, mobile-api
- Backend: user-service, order-service, notification-service
- 3 environments: dev, staging, prod
- Each team wants autonomy over their services
- Platform team needs to set base policies
Your Task
Section titled “Your Task”Part 1: Repository Decision
## Repository Strategy
How many repos? [ ] 1 (monorepo) [ ] 4 (team repos) [ ] Other: ___
Rationale:__________________________________________________________________________________________________
Who owns each repo?- Repo 1: _______________ Owner: _______________- Repo 2: _______________ Owner: _______________- ...Part 2: Directory Structure
Design the structure for one of the repos:
## Directory Structure for: _______________
repo-name/├──├──├──└──
Explain your choices:- Why this structure? _______________- How do environments differ? _______________- Where are base configs? _______________Part 3: Promotion Flow
How does a change get from dev to prod?
## Promotion Flow
1. Developer makes change: _______________2. Change gets to dev: _______________3. Promote to staging: _______________4. Promote to prod: _______________
Automation opportunities:- _______________- _______________Part 4: Access Control
## Access Control
| Repo | Read Access | Write Access | Admin ||------|-------------|--------------|-------|| | | | || | | | |
Branch protection rules:- main: _______________Success Criteria
Section titled “Success Criteria”- Chose monorepo/polyrepo with clear rationale
- Designed directory structure for at least one repo
- Defined environment promotion flow
- Documented access control approach
- Avoided branch-per-environment
Key Takeaways
Section titled “Key Takeaways”- Monorepo vs Polyrepo: Choose based on org size, team autonomy, access needs
- Separate app and config repos: Different cadences, different permissions
- Directory-per-environment: Avoid branch-per-environment pattern
- Use Kustomize base/overlays: Reduce duplication, capture differences
- CI updates Git, GitOps deploys: Clean separation of concerns
Further Reading
Section titled “Further Reading”Books:
- “GitOps and Kubernetes” — Chapter on Repository Strategies
- “Monorepo vs Polyrepo” — Various blog comparisons
Articles:
- “GitOps Repository Strategies” — Weaveworks
- “Why We Use a Monorepo” — Various tech blog posts
- “Kustomize Best Practices” — Kubernetes docs
Tools:
- Kustomize: kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/
- Helm: For chart-based templating
- yq: YAML manipulation for CI
Summary
Section titled “Summary”Repository strategy determines how smoothly GitOps operates.
Key decisions:
- Monorepo vs Polyrepo: Based on org size and autonomy needs
- App repo vs Config repo: Typically separate for GitOps
- Directory structure: Environment-based with base/overlays
- Branch strategy: Avoid branch-per-env; use directory-per-env
There’s no universally “right” answer — but there are patterns that work better for different situations. Start simple, evolve as needed.
Next Module
Section titled “Next Module”Continue to Module 3.3: Environment Promotion to learn strategies for moving changes safely through environments.
“The best repository structure is the one your team can actually follow.” — GitOps Wisdom