GitOps Reverser is a Kubernetes operator that turns live Kubernetes API activity into clean, versioned YAML in Git.
It is for teams that want API-first workflows without giving up an audit trail, reviewable history, or a repo that can later be reconciled by GitOps tooling.
The broader pattern behind this project 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.
This is advanced operator software to install, even if the downstream workflow is meant to become simpler for other teams.
| Good fit | Poor fit |
|---|---|
| Clusters where you can grant watch/RBAC, run Valkey/Redis in-cluster, and write to Git | Production HA requirements today |
| Teams that want to try API-to-Git capture first, then add 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 |
Author attribution is the only optional capability: it needs kube-apiserver audit delivery, which managed control planes (EKS/GKE/AKS) generally do not expose. Without it the operator still mirrors state, with commits authored by the configured committer. Valkey/Redis is required either way — it holds each GitTarget's watch resume state so work is re-picked up after a restart or reconnect.
- GitOps Reverser watches the Kubernetes API for the resource types each
GitTargetclaims — watch is the single source of object state. - Each change is sanitized (status,
managedFields, and runtime noise removed) and diffed against the current Git content. - Valkey/Redis tracks each GitTarget's watch resume position, so the operator re-picks up exactly where it left off after a restart or reconnect (and, when configured, holds audit attribution facts).
- The operator writes stable YAML to Git with useful commit metadata. Commits are authored by the configured committer, or by the actual user / service account when an audit fact matches (attribution — the one part that is optional).
Secret resources can be encrypted before commit with SOPS + age, Secret-shaped custom resource
types can opt into the same path at controller startup, and Git commits can be SSH-signed
through GitProvider.spec.commit.signing.
Capturing objects served by an aggregated API server is supported through the same watch path
(with a LIST fallback for servers that do not implement streaming lists); see
docs/architecture.md.
Every install needs the same base: Kubernetes watch/RBAC access, Valkey/Redis (watch resume state), Git credentials, and cert-manager. The only thing that varies is author attribution:
| Mode | Attribution | Additionally needs | Commit author |
|---|---|---|---|
| Committer-only (default) | off | — | configured committer identity |
| Attributed | on | kube-apiserver audit delivery | named user / service account on a strong match, committer otherwise |
Because object state comes from watch, GitOps Reverser is a state mirror with opportunistic
per-mutation history: it records every change it observes while watching, and collapses intermediate
versions to current state across restarts, reconnects, or 410 Gone replays. It is not a guaranteed
per-mutation change log. No path silently loses a delete — a delete missed while no watch was running is
reconciled by the replay mark-and-sweep on reconnect.
GitOps Reverser reconstructs clean Kubernetes manifests from live cluster state. It does not reconstruct higher-level authoring intent that is no longer present in the cluster.
That means it can write back stable Kubernetes YAML, but it cannot reverse Helm-rendered resources
back into a clean values.yaml, and it generally cannot infer the original authoring 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 only (
replicas=1); HA is not supported yet. - Shared-resource bi-directional workflows require explicit coordination.
- Reverse-GitOps source recovery is limited to Kubernetes manifests, not Helm/Kustomize authoring models.
- Tests run against Kubernetes
1.36. Other versions may work but are not part of the current matrix. - Runtime behavior is deterministic; there is no AI or heuristic mutation at runtime.
GitOps Reverser is a good fit for pilots, lab clusters, brownfield discovery, and design-partner users who can tolerate API and behavior changes. Production use should happen only after an environment-specific review.
Directions we may revisit later live in docs/TODO.md and docs/future/.
This quick start sets up committer-only mode: the operator mirrors watched Kubernetes state into Git, commits are authored by the configured committer identity, and named authors can be added later. It is the easiest way to prove the workflow works.
Prerequisites
- Kubernetes cluster with
kubectlconfigured - cert-manager for TLS certificate management
Managed platforms such as EKS, GKE, and AKS generally work for this committer-only flow because it does not require control-plane audit webhook configuration.
1. Install cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.19.1/cert-manager.yaml
kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=cert-manager -n cert-manager --timeout=300s2. Install Valkey with auth (required — both modes)
kubectl create namespace gitops-reverser
kubectl create secret generic valkey-auth \
--namespace gitops-reverser \
--from-literal=password="$(openssl rand -base64 32)"
helm repo add valkey https://valkey.io/valkey-helm/ && helm repo update
helm install valkey valkey/valkey --version 0.9.3 --namespace gitops-reverser \
--set auth.enabled=true \
--set auth.usersExistingSecret=valkey-auth \
--set auth.aclUsers.default.passwordKey=password \
--set "auth.aclUsers.default.permissions=~* &* +@all"3. Install GitOps Reverser
helm install gitops-reverser \
oci://ghcr.io/configbutler/charts/gitops-reverser \
--namespace gitops-reverser \
--create-namespace4. Create Git credentials
SSH deploy key example:
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
kubectl create secret generic git-creds \
--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 Git credentials Secrets are accepted as-is (they just need write access). See
docs/configuration.md for accepted Secret shapes and
docs/github-setup-guide.md for the GitHub path and HTTPS/PAT fallback.
5. Enable the starter configuration
Use the chart's quickstart values so Helm creates a starter GitProvider, GitTarget, and
WatchRule:
helm upgrade gitops-reverser \
oci://ghcr.io/configbutler/charts/gitops-reverser \
--namespace gitops-reverser \
--reuse-values \
--set quickstart.enabled=true \
--set quickstart.gitProvider.url=git@github.com:<org>/<repo>.gitThe default quickstart namespace is default, so the git-creds Secret above should exist there
unless you explicitly set quickstart.namespace to something else.
The starter GitTarget writes under live-cluster by default. That keeps the first run away from
the repository root. To deliberately target the root instead, add
--set quickstart.gitTarget.path=. to the Helm command.
Check that the starter resources become ready:
kubectl get gitprovider,gittarget,watchrule -n defaultSee docs/configuration.md for how GitProvider, GitTarget, and
WatchRule fit together after the starter install.
6. Test it
kubectl create configmap test-config --from-literal=key=value -n defaultYou should see a new commit land in your Git repository within seconds.
If no commit appears, start with:
kubectl logs -n gitops-reverser deploy/gitops-reverser
kubectl describe gitprovider,gittarget,watchrule -n defaultThe quick start deliberately avoids kube-apiserver audit delivery. To make commits use the actual Kubernetes user or service account when a strong audit match exists, enable attribution and then follow the Helm notes:
helm upgrade gitops-reverser \
oci://ghcr.io/configbutler/charts/gitops-reverser \
--namespace gitops-reverser \
--reuse-values \
--set attribution.enabled=true
helm get notes gitops-reverser -n gitops-reverserThose notes include the audit webhook URL, the client certificate Secret names, and the kubeconfig shape
that kube-apiserver needs. For networking and TLS tradeoffs around audit delivery, see
docs/design/audit-webhook-api-server-connectivity.md.
If you need exact edit authorship but do not want to operate this audit path yourself, I welcome conversations about a managed ConfigButler solution.
Start here for the stable docs surface:
docs/README.mddocs/configuration.mddocs/security-model.mddocs/commit-signing.mddocs/github-setup-guide.mddocs/sops-age-guide.mddocs/bi-directional.mddocs/alternatives.md
If you are evaluating alternatives or deciding when another approach is a better fit, start with
docs/alternatives.md.
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 behind this work.
- Connect on LinkedIn. Feedback, questions, and ideas are welcome.
Issues, docs fixes, and code contributions are welcome. See CONTRIBUTING.md.
Apache 2.0
