Replikate 1.1 — Now It Crosses Clusters
The Promise From 1.0
A few weeks ago I tagged Replikate 1.0 and made a specific commitment: the replikate.brainchurts.com/sync annotation contract was frozen, and the one big thing still missing — cross-cluster replication — would land after 1.0 as a purely additive feature. No breaking changes, no 2.0, nothing existing sources would even notice.
1.1 makes good on that. Replikate can now replicate a ConfigMap or Secret into other clusters, not just other namespaces — and a source that doesn’t ask for it behaves exactly as it did in 1.0.
How It Works
The model is hub-and-spoke. One cluster runs Replikate (the hub); the clusters it replicates into are spokes, registered by dropping a labeled credential Secret on the hub. Then you annotate a source with one new, optional line:
metadata:
annotations:
replikate.brainchurts.com/sync: "team=web" # local fan-out (unchanged)
replikate.brainchurts.com/target-clusters: "spoke-a,spoke-b" # + these clusters
sync still governs local replication exactly as before. target-clusters is additive — it names spoke clusters that also get a copy, one per cluster in the source’s own namespace. Drop a cluster from the list and its copy is pruned; delete the source and every copy, local and remote, is cleaned up.
If you’ve used AppsCode’s config-syncer, this will feel familiar on purpose: it mirrors that tool’s sync + sync-contexts split, so a migration doesn’t require rethinking your mental model. The one place I deliberately improved on it: each spoke is a separate, least-privilege credential Secret rather than one giant kubeconfig baked into the operator — smaller blast radius, rotate one without touching the others.
The Boring, Important Parts
A controller that writes into other people’s clusters has to be careful, so most of the work went into the safety rails, not the happy path:
- It won’t shoot itself. Register a credential that accidentally points back at the hub and Replikate refuses it with a
SelfClusterevent — because treating the hub as its own spoke would let it mistake its local copies for remote ones and delete them. - A down spoke can’t wedge you. Fan-out is best-effort per cluster: an unreachable or misconfigured spoke gets an event and is skipped, never blocking the local replication path or the other spokes, and every spoke request is bounded by a timeout so a hung API server can’t stall the controller. Transient failures requeue and self-heal.
- You can see it. Per-cluster metrics (
replikate_cluster_up,replikate_remote_copy_operations_total) and events on the source tell you what went where.
The Part I Wouldn’t Ship Without
The same rule from 1.0 applied here: I don’t tag a release on a path that’s never actually run. For 1.0 that meant a live webhook smoke test. For 1.1 it meant proving cross-cluster works against two genuinely separate API servers — not a mock, not a single cluster pretending.
So the release gate is an integration test that stands up two control planes, registers one as a spoke of the other, and asserts the whole loop: a source in the hub produces a copy in the spoke, dropping the target prunes it, and deleting the source cleans it up. It runs in CI. Cross-cluster isn’t “should work” — it’s tested end to end.
Honest About the Edges (Still)
1.1 is single-hub and copies land in the source’s namespace on each spoke — no remote selector fan-out yet, and no multi-hub coexistence (two hubs writing to one spoke). Those are real features, deliberately deferred rather than half-shipped: the next line of work adds an origin-cluster identity so multiple hubs can share a spoke, a sturdier same-cluster check, and native selector fan-out into spokes. All additive, all 1.x.
Get It
Replikate 1.1.0 is BSD 3-Clause at github.com/cwolsen7905/replikate, with the spoke setup in deploy/spoke-rbac.yaml and the full history in the CHANGELOG. --enable-cross-cluster, register a spoke, annotate a source — and stop copying secrets between clusters by hand.