Repository Structure
Organizing your GitOps repository is crucial for maintainability, scalability, and team collaboration. A well-structured repository makes it easy to find resources, manage environments, and implement promotion workflows.
Repository Organization Patterns
Pattern 1: App-of-Apps
The App-of-Apps pattern uses a root application that manages multiple child applications.
Structure:
repo/
├── apps/
│ ├── app1/
│ │ └── application.yaml
│ ├── app2/
│ │ └── application.yaml
│ └── app3/
│ └── application.yaml
└── root/
└── application.yaml # Root app-of-apps
Root Application (ArgoCD):
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/repo
targetRevision: main
path: apps
destination:
server: https://kubernetes.default.svc
namespace: argocd
Benefits:
- Single point of management
- Easy to add/remove applications
- Centralized configuration
Pattern 2: Monorepo
All applications and infrastructure in a single repository.
monorepo/
├── apps/
│ ├── web-app/
│ │ ├── base/
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ └── kustomization.yaml
│ │ └── overlays/
│ │ ├── dev/
│ │ ├── staging/
│ │ └── production/
│ ├── api/
│ │ └── ...
│ └── worker/
│ └── ...
├── infrastructure/
│ ├── monitoring/
│ ├── logging/
│ └── ingress/
└── clusters/
├── dev/
│ └── kustomization.yaml
├── staging/
│ └── kustomization.yaml
└── production/
└── kustomization.yaml
Benefits:
- Single source of truth
- Easy cross-references
- Atomic changes across apps
Limitations:
- Larger repository size
- Requires careful access control
- Can become complex
Pattern 3: Multi-Repo
Separate repository per application or team.
org/
├── web-app-manifests/
│ ├── base/
│ └── overlays/
├── api-manifests/
│ └── ...
├── infrastructure/
│ └── ...
└── cluster-config/
└── ...
Benefits:
- Clear ownership
- Independent versioning
- Better access control
- Smaller repositories
Limitations:
- More repositories to manage
- Cross-app changes require multiple PRs
- Potential for inconsistency
Pattern 4: Environment-Based
Organize by environment first.
repo/
├── environments/
│ ├── dev/
│ │ ├── apps/
│ │ │ ├── web-app/
│ │ │ └── api/
│ │ └── infrastructure/
│ ├── staging/
│ │ └── ...
│ └── production/
│ └── ...
└── base/
├── web-app/
└── api/
Benefits:
- Clear environment separation
- Easy environment promotion
- Environment-specific configs
Recommended Structure
Standard Monorepo Structure
kubernetes-manifests/
├── README.md
├── .github/
│ └── workflows/
├── apps/
│ ├── web-app/
│ │ ├── base/
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ ├── configmap.yaml
│ │ │ └── kustomization.yaml
│ │ └── overlays/
│ │ ├── dev/
│ │ │ ├── kustomization.yaml
│ │ │ └── configmap-patch.yaml
│ │ ├── staging/
│ │ │ └── kustomization.yaml
│ │ └── production/
│ │ └── kustomization.yaml
│ └── api/
│ └── ...
├── infrastructure/
│ ├── monitoring/
│ │ ├── prometheus/
│ │ └── grafana/
│ ├── logging/
│ │ └── loki/
│ └── ingress/
│ └── nginx/
├── clusters/
│ ├── dev/
│ │ ├── apps.yaml
│ │ └── kustomization.yaml
│ ├── staging/
│ │ └── ...
│ └── production/
│ └── ...
└── base/
└── common/
└── namespace.yaml
Kustomize Base and Overlays
Base Structure
apps/web-app/base/
├── deployment.yaml
├── service.yaml
├── configmap.yaml
└── kustomization.yaml
base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
commonLabels:
app: web-app
managed-by: kustomize
Overlay Structure
apps/web-app/overlays/dev/
├── kustomization.yaml
└── configmap-patch.yaml
overlays/dev/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: dev
resources:
- ../../base
replicas:
- name: web-app
count: 1
images:
- name: web-app
newTag: dev-latest
patches:
- path: configmap-patch.yaml
target:
kind: ConfigMap
name: web-app-config
overlays/dev/configmap-patch.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: web-app-config
data:
environment: dev
log_level: debug
Directory Layout Examples
Example 1: Simple Structure
manifests/
├── apps/
│ ├── app1.yaml
│ └── app2.yaml
└── infrastructure/
└── monitoring.yaml
Example 2: Kustomize Structure
manifests/
├── apps/
│ └── web-app/
│ ├── base/
│ └── overlays/
│ ├── dev/
│ └── prod/
└── infrastructure/
└── ...
Example 3: Helm Structure
manifests/
├── apps/
│ └── web-app/
│ ├── values-dev.yaml
│ ├── values-staging.yaml
│ └── values-prod.yaml
└── charts/
└── web-app/
└── ...
Secrets Management
Approach 1: External Secrets Operator
manifests/
├── apps/
│ └── web-app/
│ ├── deployment.yaml
│ └── external-secret.yaml
└── secrets/
└── templates/
└── web-app-secret.yaml
Approach 2: Sealed Secrets
manifests/
├── apps/
│ └── web-app/
│ ├── deployment.yaml
│ └── sealed-secret.yaml
Approach 3: Separate Secrets Repo
Keep secrets in a separate, private repository:
secrets-repo/
├── dev/
│ └── web-app-secret.yaml
└── production/
└── web-app-secret.yaml
Best Practices
1. Use Kustomize for Environment Differences
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
replicas:
- name: web-app
count: 5
images:
- name: web-app
newTag: v1.2.3
2. Separate Base from Overlays
- Base - Common configuration
- Overlays - Environment-specific changes
3. Use Consistent Naming
apps/
├── web-app/ # kebab-case
├── api-service/ # descriptive
└── worker-job/ # clear purpose
4. Document Structure
Create README files:
apps/web-app/
├── README.md
├── base/
└── overlays/
README.md:
# Web App
Application for serving web traffic.
## Structure
- `base/` - Base manifests
- `overlays/` - Environment-specific configs
## Deployment
Deploy to dev:
```bash
kubectl apply -k overlays/dev
### 5. Version Control Best Practices
- Use meaningful commit messages
- Tag releases
- Use branches for features
- Protect main branch
### 6. Keep Manifests DRY
Use Kustomize or Helm to avoid duplication:
```yaml
# Bad: Duplicated manifests
apps/web-app/dev/deployment.yaml
apps/web-app/staging/deployment.yaml
apps/web-app/prod/deployment.yaml
# Good: Base + overlays
apps/web-app/base/deployment.yaml
apps/web-app/overlays/dev/kustomization.yaml
apps/web-app/overlays/prod/kustomization.yaml
Common Anti-Patterns
Anti-Pattern 1: Flat Structure
# Bad
manifests/
├── app1-dev.yaml
├── app1-prod.yaml
├── app2-dev.yaml
└── app2-prod.yaml
Problem: Hard to maintain, lots of duplication
Solution: Use base + overlays
Anti-Pattern 2: No Separation
# Bad
manifests/
├── everything.yaml
Problem: Hard to find resources, large files
Solution: Organize by app and resource type
Anti-Pattern 3: Hardcoded Values
# Bad
apiVersion: apps/v1
kind: Deployment
spec:
replicas: 3 # Hardcoded
template:
spec:
containers:
- image: myapp:latest # Hardcoded tag
Problem: Can’t vary by environment
Solution: Use Kustomize or Helm
Anti-Pattern 4: Secrets in Git
# Bad - Never do this!
apiVersion: v1
kind: Secret
data:
password: cGFzc3dvcmQ= # Base64 encoded, but still in Git
Problem: Security risk
Solution: Use Sealed Secrets, External Secrets, or separate secrets repo
Repository Structure for ArgoCD
ArgoCD App-of-Apps
repo/
├── apps/
│ ├── web-app/
│ │ └── application.yaml
│ └── api/
│ └── application.yaml
└── root/
└── application.yaml
ArgoCD ApplicationSet
repo/
├── apps/
│ ├── web-app/
│ │ ├── base/
│ │ └── overlays/
│ └── api/
│ └── ...
└── applicationset.yaml
Repository Structure for Flux
Flux Structure
repo/
├── clusters/
│ └── production/
│ ├── flux-system/
│ └── apps/
│ └── web-app/
│ └── kustomization.yaml
└── apps/
└── web-app/
├── base/
└── overlays/
Flux Kustomization
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: web-app
namespace: flux-system
spec:
interval: 5m0s
path: ./apps/web-app/overlays/production
prune: true
sourceRef:
kind: GitRepository
name: manifests
Summary
A well-organized GitOps repository:
- Separates concerns - Apps, infrastructure, clusters
- Uses base + overlays - Avoids duplication
- Organizes by purpose - Easy to find resources
- Manages secrets securely - Never in Git
- Documents structure - README files
- Follows conventions - Consistent naming
Choose a structure that fits your team:
- Monorepo - Single repository, easier collaboration
- Multi-repo - Separate repos, better isolation
- App-of-Apps - Centralized management
- Environment-based - Clear environment separation
The key is consistency and maintainability. Start simple and evolve as needed.