Replikate — ConfigMap and Secret Replication Across Kubernetes Namespaces
The Gap Everyone Hits Eventually
If you run anything non-trivial on Kubernetes, you’ve hit this wall: a ConfigMap or Secret is namespace-scoped, and there’s no built-in way to share one across namespaces. A registry pull secret, a CA bundle, a shared config block, a wildcard TLS cert — the moment you have more than a couple of namespaces, you need the same object in all of them, and Kubernetes gives you nothing.
So people paper over it. A kubectl apply loop in CI. A Helm chart that stamps the secret into every release. A hand-maintained list of namespaces that’s wrong the day someone adds a new one. All of these share the same failure mode: they’re push-once solutions to a keep-in-sync problem. The source changes and the copies rot. A new namespace appears and nobody remembers to seed it.
I got tired of writing the loop. So I wrote Replikate.
What It Does
Replikate is a lightweight Kubernetes controller that replicates ConfigMaps and Secrets across namespaces, driven entirely by an annotation on the source object. You annotate the thing you want shared, and Replikate maintains a managed copy of it everywhere you asked for:
apiVersion: v1
kind: ConfigMap
metadata:
name: shared-config
namespace: default
annotations:
replikate.brainchurts.com/sync: "team=web" # "" for all namespaces
data:
LOG_LEVEL: "info"
The annotation value is the whole interface:
- A label selector (
team=web) → copies land in every namespace whose labels match. - An empty string (
"") → copies land in every namespace, cluster-wide.
And then it actually keeps them in sync, which is the part the kubectl loops never do:
- Source changed → every copy is updated to match.
- Source deleted, or the annotation removed → the copies are cleaned up, via a finalizer, so nothing is left orphaned.
- A new namespace appears that matches the selector → it gets populated automatically, no human in the loop.
That last one is the whole point. Replikate turns “share this everywhere” from a thing you do into a thing that’s simply true and stays true.
The Design Decisions That Matter
A controller that writes objects into every namespace in your cluster is a controller that can do a lot of damage if it’s careless. Two decisions define the whole safety posture:
It never touches an object it doesn’t own. Every copy Replikate creates is stamped with replikate.brainchurts.com/managed-by=replikate plus origin labels pointing back at the source. Before it writes, it checks that stamp. If a ConfigMap named shared-config already exists in a target namespace and you made it by hand, Replikate leaves it alone — it will not clobber an object it doesn’t manage. The blast radius is strictly the set of objects it created itself.
Cleanup is a finalizer, not a hope. When you delete the source or pull the annotation, the copies have to go too — otherwise you’ve traded a sync problem for a garbage problem. Replikate uses a Kubernetes finalizer on the source so deletion blocks until the managed copies are actually removed. Cleanup is part of the delete, not a background cron that might run.
How It’s Built
Replikate is written in Go on top of sigs.k8s.io/controller-runtime (the same foundation Kubernetes’ own operators are built on), targeting Kubernetes 1.31. The shape is deliberately small: a shared syncer holds the reconciliation logic — resolve the selector to a namespace set, diff desired copies against actual, create/update/prune — and two thin controllers (one for ConfigMaps, one for Secrets) feed it. The core is a few hundred lines. A controller you can read in one sitting is a controller you can trust in production.
The operational packaging is intentionally boring, which is the right kind of boring for something that touches every namespace:
- Ships as a
distroless/static:nonrootcontainer — no shell, no package manager, minimal attack surface. - Installs via a Helm chart that carries the Deployment, ServiceAccount, and a tightly-scoped ClusterRole/RoleBinding.
- CI (GitHub Actions) builds and pushes the image on merge to
main.
Honest About the Edges
It’s an MVP, and I’d rather name the limits than pretend they aren’t there:
- It replicates ConfigMaps and Secrets intra-cluster only. Cross-cluster sync is a planned follow-up, not a shipped feature.
- If you hand-edit a managed replica, it’s corrected on the next source change or the controller’s periodic resync — not instantly.
- Two sources with the same name in different namespaces that both target a third namespace will collide on name. Give such sources distinct names.
None of these are hard blockers for the job it’s built for — they’re the honest scope of a v0.
Get It
Replikate is BSD 3-Clause licensed and on GitHub at github.com/cwolsen7905/replikate. Annotate a source, helm install, and stop writing the kubectl loop.
It’s a small tool that does one irritating thing well — which is exactly the kind of tool I most enjoy having around.