Stop storing abstractions. Start storing truth.
A reference implementation of the Rendered Manifests Pattern β a GitOps strategy that eliminates the gap between what you store in Git and what actually runs in your Kubernetes cluster.
Popular GitOps engines like Argo CD and Flux support Helm and Kustomize natively β you store a chart reference or overlay in Git, and the engine renders it at apply time. This feels convenient, but it creates a critical blind spot:
- Upgrading a Helm chart or changing a single value can cascade into dozens of manifest changes that are invisible until they hit your cluster.
- PR reviewers can't see the actual diff β only the abstraction that produces the diff.
- The "source of truth" in Git isn't the truth at all β it's a recipe.
Git (abstractions) Cluster (rendered reality)
ββββββββββββββββ ββββββββββββββββββββββββββββ
β chart: v1.2 β ββ??βββΊ β Deployment, Service, β
β values.yaml β β ConfigMap, RBAC, ... β
ββββββββββββββββ ββββββββββββββββββββββββββββ
β
What reviewers see What actually changes
The Rendered Manifests Pattern ensures Git always contains fully-hydrated, plain YAML manifests β the same YAML that kubectl apply will use. Helm and Kustomize are still used, but only in a CI rendering step before anything reaches Git.
main branch env/dev branch Cluster
ββββββββββββββββ CI/CD ββββββββββββββββββββββββ GitOps ββββββββββββββ
β Kustomize β βββββββΊ β deployment.yaml β ββββββββΊ β Kubernetes β
β overlays β render β service.yaml β β β
β values.yaml β β configmap.yaml β ββββββββββββββ
ββββββββββββββββ ββββββββββββββββββββββββ
β
Reviewers see exact YAML diffs
Every change to deployed resources is visible as a plain YAML diff in Git β no reasoning required about how an overlay or values change cascades through a chart.
.
βββ main # Source branch β Kustomize overlays & config
β βββ base/ # Shared base manifests
β βββ overlays/
β β βββ dev/ # Dev environment overlays
β β βββ staging/ # Staging environment overlays
β β βββ prod/ # Production environment overlays
β βββ .github/workflows/ # CI pipeline for rendering
β
βββ env/dev # Rendered branch β plain YAML for dev
βββ env/staging # Rendered branch β plain YAML for staging
βββ env/prod # Rendered branch β plain YAML for prod
Source configuration lives on main. Rendered manifests are committed to environment-specific branches by CI β never by hand.
1. Developer opens a PR against main
β
βΌ
2. CI renders manifests (kustomize build / helm template)
β
βΌ
3. Rendered YAML diff is visible in the PR β reviewers see exactly
what will change in the cluster
β
βΌ
4. PR is merged to main
β
βΌ
5. CI commits rendered manifests to env/* branches
β
βΌ
6. Argo CD detects the change in env/* and syncs the cluster
The rendering step is fully automated. Developers never manually touch the rendered branches.
| Concern | Without Rendered Manifests | With Rendered Manifests |
|---|---|---|
| PR visibility | Reviewers see overlays/values, not the actual diff | Reviewers see exact YAML changes |
| Audit log | Git history reflects config abstractions | Git history is a full audit trail of every deployed resource |
| Tooling flexibility | GitOps engine must support your tool | Any tool that outputs YAML works (Helm, Kustomize, Timoni, CUEβ¦) |
| Drift detection | Engine re-renders at sync time | Git is the immutable source of truth β no runtime surprises |
| Security | Mutations can happen at apply time | What's in Git is exactly what runs |
kubectlconfigured against your target clusterkustomizeCLI (orhelmfor Helm-based setups)- A Kubernetes GitOps platform: Argo CD or Flux
- (Optional) Kargo for progressive multi-stage promotion
# 1. Fork this repository, then clone your fork
git clone https://github.com/<your-username>/rendered-manifest-pattern.git
cd rendered-manifest-pattern
# 2. Run the personalization script
./personalize.sh <your-github-username>
# 3. Commit and push
git add -A
git commit -m "chore: personalize manifests"
git pushPoint your Argo CD Application resources at the rendered branches, not main:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook-dev
spec:
source:
repoURL: https://github.com/<your-username>/rendered-manifest-pattern
targetRevision: env/dev # β rendered branch, not main
path: .
destination:
server: https://kubernetes.default.svc
namespace: guestbook-devPush a change to main to kick off the CI rendering pipeline. GitHub Actions will render the Kustomize overlays and commit plain YAML to each env/* branch automatically.
The rendering workflow in .github/workflows/render.yaml runs on every push to main:
jobs:
render:
strategy:
matrix:
env: [dev, staging, prod]
steps:
- name: Render manifests
run: kustomize build overlays/${{ matrix.env }} > /tmp/rendered.yaml
- name: Commit to env branch
run: |
git switch env/${{ matrix.env }}
cp /tmp/rendered.yaml .
git commit -am "render: update from $(git rev-parse --short HEAD)"
git pushThis pattern pairs naturally with Kargo for controlled, multi-stage promotion. Instead of CI pushing directly to all environment branches simultaneously, Kargo provides a pipeline that gates promotion based on health checks and approvals:
main ββrenderβββΊ env/dev ββapproveβββΊ env/staging ββapproveβββΊ env/prod
See the companion kargo-rendered-branches repository for a full Kargo-based example.
This pattern is a deliberate tradeoff. Be aware of the following:
Things that change:
- Helm lifecycle hooks (e.g.,
pre-install,post-upgrade) are not available because Helm isn't run at apply time. Argo CD sync waves or hooks can often fill this role. - Kubernetes Secrets should not be stored as rendered YAML. Use External Secrets Operator or a similar runtime injection approach.
- Rendered branches accumulate YAML files over time, which can increase repository size and diff noise in long-running projects.
Things that get much better:
- Every infrastructure change becomes a reviewable, auditable Git commit.
- Cluster state is always a direct reflection of what's in a Git branch β no templating engine stands between them.
- Any manifest-generating tool can be used without needing GitOps engine support.
- π The Rendered Manifests Pattern β original blog post by Akuity
- π₯ KubeCon Talk: Reveal Your True Desired State β Nicholas Morey, Akuity
- π§ Kargo β progressive delivery and promotion for GitOps
- π§ kargo-rendered-branches β Kargo example using this pattern
- π§ Argo CD Source Hydrator β in-cluster rendering alternative (alpha)
- π Akuity Advanced GitOps Workshop β hands-on tutorial
Contributions, issues, and feature requests are welcome. Please open an issue to discuss significant changes before submitting a pull request.
Apache 2.0 β see LICENSE for details.