Explanation
Extra Manifests Organization Strategy¶
Learn why and how the KUP6S extra manifests are organized into semantic groups.
The Problem¶
Infrastructure-as-code manifests can become difficult to manage as projects grow. Common challenges include:
Hidden Dependencies: Numeric ordering (01, 02, 03…) obscures functional relationships
Insertion Conflicts: Adding new manifests requires renumbering to maintain order
Unclear Grouping: Related components scattered across different numbers
Scalability: Running out of numbers in tightly-packed sequences
Example of the old system:
01-namespace.yaml
02-csi-driver-smb.yaml
03-smb-storageclass.yaml
04-cert-manager.yaml
...
18-external-secrets-operator.yaml
What happens when you need to add another storage component between 03 and 04? Renumber everything? Create 03b? Neither scales well.
The Solution: Semantic Grouping¶
KUP6S reorganized manifests into semantic groups with letter-based sub-ordering:
GROUP-LETTER-description.yaml.tpl
Where:
GROUP: Numeric group (10, 20, 30, etc.) representing functional category
LETTER: Uppercase letter (A, B, C, D…) for ordering within group
description: Descriptive component name
Example of the new system:
10-A-namespaces.yaml.tpl # Group 10: Foundation
20-A-cert-manager.yaml.tpl # Group 20: Security
20-B-external-secrets-operator.yaml.tpl
20-C-application-secrets.yaml.tpl
30-A-traefik-basicauth.yaml.tpl # Group 30: Networking
30-B-traefik-dashboard.yaml.tpl
40-A-csi-driver-smb.yaml.tpl # Group 40: Storage
40-B-smb-storageclass.yaml.tpl
...
Group Definitions¶
Group 10 - Foundation¶
Purpose: Core infrastructure that must exist before anything else
Components:
Namespaces for all infrastructure services
Why first: All namespaced Kubernetes resources require their namespace to exist
Group 20 - Security & Secrets¶
Purpose: Security infrastructure and secret management
Components:
cert-manager (TLS certificates)
External Secrets Operator (secret synchronization)
ClusterSecretStore resources
Why second: Security components should be established before networking and applications
Critical ordering: ESO operator (20-B) must deploy before ClusterSecretStore (20-C)
Group 30 - Networking¶
Purpose: Ingress controllers and network infrastructure
Components:
Traefik authentication middleware
Traefik dashboard ingress
Why after security: Ingresses require TLS certificates from cert-manager (Group 20)
Group 40 - Storage¶
Purpose: Persistent storage infrastructure
Components:
SMB CSI driver for Hetzner Storage Box
SMB StorageClass
Longhorn backup configuration
Longhorn recurring backups
Longhorn ingress
Longhorn storage classes
Why mid-stack: Storage is independent but needed before stateful applications
Group 50 - Provisioning¶
Purpose: Infrastructure-as-code operators for dynamic resource provisioning
Components:
Crossplane operator
Crossplane AWS S3 provider (configured for Hetzner)
etcd backup bucket (DR region)
Loki S3 bucket (production region)
Why before observability: Monitoring stack (Group 70) needs S3 buckets for Loki logs
Critical ordering: Crossplane (50-A) → Provider (50-B) → Buckets (50-C, 50-D)
Group 70 - Observability¶
Purpose: Monitoring, logging, and alerting infrastructure
Components:
kube-prometheus-stack (Prometheus, Grafana, Loki, Alertmanager, Alloy)
Why after provisioning: Loki requires S3 bucket from Group 50
Group 80 - GitOps¶
Purpose: Declarative application deployment tools
Components:
ArgoCD for GitOps-based deployments
Why last: ArgoCD deploys applications after infrastructure is ready
Benefits¶
1. Clear Functional Grouping¶
Related components naturally cluster together:
40-A-csi-driver-smb.yaml.tpl
40-B-smb-storageclass.yaml.tpl
40-C-longhorn-backup-secret.yaml.tpl
40-D-longhorn-recurring-backup.yaml.tpl
40-E-longhorn-ingress.yaml.tpl
40-F-longhorn-storage-classes.yaml.tpl
All storage components are in Group 40, making it obvious they’re related.
2. Easy Insertion¶
Adding a new component doesn’t require renumbering:
Old system (need to add cert-renewer between cert-manager and traefik):
04-cert-manager.yaml # Exists
04b-cert-renewer.yaml? # Ugly workaround
05-traefik-basicauth.yaml # Or renumber everything below?
New system (just pick next letter in Group 20):
20-A-cert-manager.yaml.tpl
20-B-external-secrets-operator.yaml.tpl
20-C-application-secrets.yaml.tpl
20-D-cert-renewer.yaml.tpl # ← Clean insertion
3. Explicit Dependencies¶
Group numbers make cross-group dependencies obvious:
# 70-A-kube-prometheus-stack.yaml.tpl
# Dependencies:
# - 50-D-loki-s3-bucket.yaml.tpl (S3 storage for Loki)
Group 70 depending on Group 50 is immediately clear: observability needs provisioning.
4. Room to Grow¶
Each group has 26 available letters (A-Z). That’s plenty of space for future additions without renumbering.
Want to add monitoring tools? Just use 70-B, 70-C, 70-D…
5. Better Git History¶
When adding new manifests, git diffs show only the new file, not renumbering changes:
Old system:
- 05-traefik.yaml
+ 05-cert-renewer.yaml
+ 06-traefik.yaml # Renamed - git shows deletion+addition
New system:
+ 20-D-cert-renewer.yaml # Clean addition, no renames
Migration from Old to New¶
The complete rename mapping:
Old |
New |
Group |
|---|---|---|
01-namespace.yaml |
10-A-namespaces.yaml.tpl |
Foundation |
04-cert-manager.yaml |
20-A-cert-manager.yaml.tpl |
Security |
18-external-secrets-operator.yaml |
20-B-external-secrets-operator.yaml.tpl |
Security |
09-application-secrets-eso.yaml |
20-C-application-secrets.yaml.tpl |
Security |
05-traefik-basicauth.yaml |
30-A-traefik-basicauth.yaml.tpl |
Networking |
06-traefik-dashboard.yaml |
30-B-traefik-dashboard.yaml.tpl |
Networking |
02-csi-driver-smb.yaml |
40-A-csi-driver-smb.yaml.tpl |
Storage |
03-smb-storageclass.yaml |
40-B-smb-storageclass.yaml.tpl |
Storage |
11-longhorn-backup.yaml |
40-C-longhorn-backup-secret.yaml.tpl |
Storage |
10-longhorn-recurring-backup.yaml |
40-D-longhorn-recurring-backup.yaml.tpl |
Storage |
12-longhorn-ingress.yaml |
40-E-longhorn-ingress.yaml.tpl |
Storage |
13-longhorn-storage-classes.yaml |
40-F-longhorn-storage-classes.yaml.tpl |
Storage |
07-crossplane.yaml |
50-A-crossplane.yaml.tpl |
Provisioning |
08-crossplane-provider-aws-s3.yaml |
50-B-crossplane-provider-s3.yaml.tpl |
Provisioning |
(new) |
50-C-etcd-backup-bucket.yaml.tpl |
Provisioning |
09-loki-s3-bucket.yaml |
50-D-loki-s3-bucket.yaml.tpl |
Provisioning |
14-kube-prometheus-stack.yaml |
70-A-kube-prometheus-stack.yaml.tpl |
Observability |
16-argocdchart.yaml |
80-A-argocd.yaml.tpl |
GitOps |
Note: All files renamed using git mv to preserve history.
Design Philosophy¶
Why Groups of 10?¶
Groups increment by 10 (10, 20, 30…) to leave room for future category insertion:
10 - Foundation
20 - Security
25 - ??? (future category between Security and Networking)
30 - Networking
This is unlikely to be needed, but the flexibility costs nothing.
Why Uppercase Letters?¶
Uppercase letters (A, B, C) provide better visual distinction in file listings compared to lowercase:
Better readability:
40-A-csi-driver-smb.yaml.tpl
40-B-smb-storageclass.yaml.tpl
vs.
40-a-csi-driver-smb.yaml.tpl # Lowercase harder to spot
40-b-smb-storageclass.yaml.tpl
Why .tpl Extension?¶
The .tpl extension explicitly marks files as OpenTofu templates containing variable placeholders like ${variable}.
This prevents accidentally treating them as direct kubectl-applicable manifests.
Best Practices¶
Adding a New Manifest¶
Choose the right group: What functional category does this belong to?
Check dependencies: Does it depend on other groups? Should it go after them?
Pick next letter: Find the next available letter in that group
Use descriptive names:
50-B-crossplane-provider-s3.yaml.tplis better than50-B-cp.yaml.tplUpdate kustomization.yaml.tpl: Add to appropriate group section
Document: Add entry to extra-manifests.md
Respecting Group Boundaries¶
Don’t: Mix components from different functional groups
Bad:
40-A-csi-driver-smb.yaml.tpl # Storage
40-B-smb-storageclass.yaml.tpl # Storage
40-C-prometheus.yaml.tpl # ❌ Monitoring - wrong group!
Do: Keep groups functionally cohesive
Good:
40-A-csi-driver-smb.yaml.tpl # Storage
40-B-smb-storageclass.yaml.tpl # Storage
...
70-A-kube-prometheus-stack.yaml.tpl # ✅ Monitoring in correct group
Ordering Within Groups¶
Letters (A, B, C…) indicate deployment order within a group. Respect dependencies:
50-A-crossplane.yaml.tpl # Operator first
50-B-crossplane-provider-s3.yaml.tpl # Provider second (needs operator)
50-C-etcd-backup-bucket.yaml.tpl # Bucket third (needs provider)
50-D-loki-s3-bucket.yaml.tpl # Another bucket (needs provider)
See Also¶
Extra Manifests - Complete manifest inventory
Apply Infrastructure Changes - How to deploy manifest changes
Tutorial: First Deployment - Hands-on cluster creation