GitOps Reverser is a Kubernetes operator that turns live Kubernetes API activity into clean, versioned YAML in Git: an audit trail, reviewable history, and a GitOps-reconcilable repo, without giving up API-first workflows. The broader pattern is described at reversegitops.dev.
Want proof? See this example commit in ConfigButler/example-audit.
- Keep using the Kubernetes API as the write path.
- Capture those live changes as stable manifests in Git.
- Keep configuration file-backed, reviewable, and reusable.
| Good fit | Poor fit |
|---|---|
| Clusters where you can grant watch/RBAC and write to Git (optionally run Valkey/Redis for the full feature set) | Production HA requirements today |
| Teams that want API-to-Git capture first, then named author attribution later | Shared paths with two always-on writers fighting over the same resources |
| API-first or hybrid teams that still want Git history; brownfield discovery, hotfix capture, migration toward GitOps | Workflows that need a guaranteed per-mutation change log rather than a state mirror |
- Watches the Kubernetes API for the resource types each
GitTargetclaims. Watch is the single source of object state. - Sanitizes each change (status,
managedFields, and runtime noise removed) and diffs it against the current Git content. - Writes stable YAML to Git with useful commit metadata, authored according to the mode below.
It can also:
- Encrypt
Secretvalues before commit with SOPS + age (Secret-shaped custom resources can opt in). - SSH-sign every commit via
GitProvider.spec.commit.signing. - Group changes within a time window into a single commit.
- Take your own commit message and "why" from a
CommitRequest.
Every install shares one base: Kubernetes watch/RBAC access, Git credentials, and cert-manager. The only thing that varies is who shows up as the commit author.
| Mode | Git author | Git committer | Also needs |
|---|---|---|---|
configured-author (default) |
the configured identity | the configured identity | None |
attributed-author |
the authenticated Kubernetes actor | the configured identity | audit delivery + Valkey/Redis |
Every commit carries both a Git author and a Git committer. In configured-author mode they are
the same configured identity (say, ConfigButler Bot). Turn on attribution.enabled and only the
author changes; it becomes whoever actually made the change, while the committer stays put:
| Who made the change (Kubernetes actor) | Git author | Git committer |
|---|---|---|
Human user (simon@example.com) |
Simon | ConfigButler Bot |
Service account (system:serviceaccount:team-a:deployer) |
the team-a/deployer service account |
ConfigButler Bot |
| CI / GitHub App identity | that CI / App identity | ConfigButler Bot |
The committer column never moves. That is the point. On a strong audit match the actor becomes the
author; with no match, the commit still lands, authored by the committer. attributed-author needs
kube-apiserver audit delivery (which managed control planes like EKS/GKE/AKS generally do not expose)
plus Valkey/Redis. See the attribution setup guide.
Valkey/Redis is optional but advised. The default runs without it in configured-author mode.
Add a reachable Valkey/Redis to unlock warm-restart cursors (watches resume instead of cold-replaying),
CommitRequest author capture (the admission webhook is installed by default but only records authors
once Redis is present, a form of author attribution), and attributed-author mode. configured-author
needs none of it.
Because object state comes from watch, GitOps Reverser is a state mirror: it reflects current
object state, not every intermediate mutation, and no delete is silently lost (one missed while no
watch was running is reconciled on reconnect). See docs/architecture.md for
replay and 410 Gone details.
GitOps Reverser reconstructs clean Kubernetes manifests from live cluster state. It does not
reconstruct higher-level authoring intent that is no longer in the cluster: it writes back stable
Kubernetes YAML, but it cannot reverse Helm-rendered resources into a clean values.yaml, and it
generally cannot infer the original structure of arbitrary templates or overlays.
That boundary is intentional. The goal is deployable cluster intent in Git, not magical recovery of every upstream abstraction.
Early-stage software; CRDs and behavior may still change.
- Single controller pod (
replicas=1); HA is not supported yet. - Shared-resource bi-directional workflows need explicit coordination.
- Source recovery is limited to Kubernetes manifests, not Helm/Kustomize authoring models.
- Tested against Kubernetes
1.36; other versions may work but are not in the matrix. - Runtime behavior is deterministic: no AI or heuristic mutation at runtime.
Good fit for pilots, lab clusters, brownfield discovery, and design partners who can tolerate change. Production use should follow an environment-specific review. Deferred directions live in docs/TODO.md and docs/future/.
This brings up the demo: a starter GitProvider, GitTarget, and WatchRule in a
gitops-reverser-quickstart-demo namespace. It watches ConfigMaps in that namespace and writes them
to <your-repo>/live-cluster on the main branch. It runs in configured-author mode (one
committer identity, no Redis) by default.
Prerequisites: a Kubernetes cluster with kubectl, Helm 3, and cert-manager for TLS.
1. Install cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.20.3/cert-manager.yaml
kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=cert-manager -n cert-manager --timeout=300s2. Create a Git repo and a deploy key
Create an (empty) repository the operator will write to, then generate a key. Add the public half as a deploy key with write access; the private half becomes the Secret in the next step:
ssh-keygen -t ed25519 -C "gitops-reverser@cluster" -f /tmp/gitops-reverser-key -N ""
# Add /tmp/gitops-reverser-key.pub to your Git provider as a deploy key (write access)3. Create the demo namespace and Git credentials
Do this before installing. The starter GitProvider is only re-checked about every 5 minutes, so
if the Secret is missing at install time your first commit can be minutes late; having it ready up
front lets the starter resources go Ready on the first reconcile:
kubectl create namespace gitops-reverser-quickstart-demo \
--dry-run=client -o yaml | kubectl apply -f -
kubectl create secret generic git-creds \
--namespace gitops-reverser-quickstart-demo \
--from-file=ssh-privatekey=/tmp/gitops-reverser-key \
--from-literal=known_hosts="$(ssh-keyscan github.com 2>/dev/null)" \
--dry-run=client -o yaml | kubectl apply -f -SSH host-key verification fails closed, so the known_hosts line is required. Existing Flux or Argo CD
credentials Secrets are accepted as-is (they must have write access). See
docs/configuration.md for accepted Secret shapes and
docs/github-setup-guide.md for the full GitHub guide and HTTPS/PAT
fallback.
4. Install GitOps Reverser with the demo
A single install enables the demo and points the starter GitProvider at your repo:
helm install gitops-reverser \
oci://ghcr.io/configbutler/charts/gitops-reverser \
--namespace gitops-reverser \
--create-namespace \
--set quickstart.enabled=true \
--set-string quickstart.gitProvider.url=git@github.com:OWNER/REPO.gitReplace OWNER/REPO with your repository (angle brackets would be parsed by the shell).
This runs without Redis, so warm-restart cursors and author capture (CommitRequest and
attributed-author) stay inactive; the operator is healthy regardless. To enable them, point it at a
reachable Valkey/Redis with --set queue.redis.addr=HOST:PORT (add
--set queue.redis.auth.existingSecret=SECRET if it requires auth; see the
chart README).
The starter GitTarget has SOPS encryption enabled, so on first reconcile the controller generates a
sops-age-key Secret in the demo namespace and annotates it with a backup reminder. That is expected;
it only matters once you mirror Secret resources (the demo watches ConfigMaps). Back up that key if
you keep the demo around.
Check the starter resources become ready:
kubectl get gitprovider,gittarget,watchrule -n gitops-reverser-quickstart-demo5. Test it
kubectl create configmap test-config --from-literal=key=value -n gitops-reverser-quickstart-demoA new commit should land in your repository within seconds. If none appears:
kubectl logs -n gitops-reverser deploy/gitops-reverser
kubectl describe gitprovider,gittarget,watchrule -n gitops-reverser-quickstart-demoTo tear the demo down: helm uninstall gitops-reverser -n gitops-reverser and
kubectl delete namespace gitops-reverser-quickstart-demo.
Every Git commit has both an author and a committer. Turn on attributed-author mode and the
real Kubernetes actor (user, service account, or CI identity) becomes the Git author, while the
configured identity stays the committer. It needs kube-apiserver audit delivery and Valkey/Redis; the
attribution setup guide walks through the setup.
ConfigButler can run a small, secure, public-facing Kubernetes API for you: we operate GitOps Reverser and authorize your end users, with forward-deployed engineers to get you started. You keep a clean, self-owned Git repo where your users express their intent.
Start with the stable docs surface:
docs/README.mddocs/configuration.mddocs/attribution-setup-guide.mddocs/security-model.mddocs/rbac.md: the two ClusterRoles, and how to stop the reverser enumerating Secretsdocs/commit-signing.mddocs/github-setup-guide.mddocs/sops-age-guide.mddocs/bi-directional.mddocs/alternatives.md: nearby tools and when another approach fits better
If this workflow matches a real problem, feedback is very welcome. The most useful reports are install attempts, first-commit experience, audit delivery issues, Git output shape, CRD ergonomics, and security or operational concerns.
- Read the Reverse GitOps manifesto for the broader pattern.
- Connect on LinkedIn. Feedback, questions, and ideas are welcome.
Issues, docs fixes, and code contributions are welcome. See CONTRIBUTING.md.
Apache 2.0
