A Kubernetes operator for Redis built around the same control-plane ideas used by CloudNativePG: direct pod/PVC management, a split control/data architecture, and deterministic failover behavior.
- Manages Redis clusters through Kubernetes CRDs (
RedisCluster,RedisBackup,RedisScheduledBackup) - Handles bootstrap, scaling, rolling updates, and failover
- Exposes stable service endpoints per cluster:
- Standalone/sentinel modes:
<name>-leader(current primary)<name>-replica(replicas)<name>-any(all data pods)<name>-sentinel(sentinel mode only)
- Cluster mode:
<name>-any(all data pods)<name>-cluster(headless cluster discovery/bus service)
- See docs/service-contract.md for the full service/label integration contract (which to use for writes, reads, and discovery)
- Standalone/sentinel modes:
- Optionally publishes a connection Secret (
spec.connectionSecret) with ready-to-use host/port/url/password and per-role endpoints; see docs/connection-secret.md - Supports backup and scheduled backup workflows
- Includes operational runbooks for common failure scenarios
This operator follows the same core pattern as CNPG:
- No StatefulSet for data pods; the reconciler manages Pods and PVCs directly
- Single binary, two roles:
redis-operator controller(controller-manager)redis-operator instance(in-pod instance manager as PID 1)
- Instance managers report per-pod status and execute pod-local operations over HTTP
- Controller remains authoritative for desired state via CRD status/spec
Why this matters: the operator can enforce strict pod update ordering, explicit fencing, and controlled failover/switchover sequences instead of delegating behavior to generic controllers.
- Replication topology: primary + replicas (Redis native replication)
- Failover safety:
- Fencing-first failover flow to reduce split-brain risk
- Boot-time split-brain guard in instance manager: non-primary pods always come up as replicas of
status.currentPrimary
- Rolling updates:
- Replicas updated first
- Primary updated last with switchover sequencing
- Secrets/config:
- If
spec.authSecretis omitted, the operator creates<cluster>-auth, persistsspec.authSecret, and enforces Redis authentication - Secrets are mounted via projected volumes and tracked with resource versions in status
- Secret updates trigger reconciliation and in-pod config refresh logic
- If
- TLS:
- Encrypted Redis traffic in all modes (
standalone,sentinel,cluster) viaspec.tlsSecret+spec.caSecret - Encrypts replication and, in sentinel mode, Sentinel monitoring and operator→Sentinel queries; see docs/tls.md
- Encrypted Redis traffic in all modes (
| Mode | Status |
|---|---|
standalone |
Supported |
sentinel |
Supported (requires at least 3 data instances) |
cluster |
Supported |
A running standalone cluster can be upgraded to sentinel (HA) in place by
editing spec.mode (and scaling spec.instances to at least 3) — the existing
primary and its data are preserved, with no backup/restore needed. Only
standalone -> sentinel is supported in place; see
docs/runbooks/standalone-to-sentinel-migration.md.
Set a release version once:
VERSION="v0.2.4"Option A: one command with Kustomize (CRDs + RBAC + controller + webhooks).
kubectl apply -k "github.com/howl-cloud/redis-operator/config/default?ref=${VERSION}"Option B: Helm OCI chart.
Charts are published to GHCR at ghcr.io/howl-cloud/charts/redis-operator on version tags.
Install CRDs first:
kubectl apply -f "https://raw.githubusercontent.com/howl-cloud/redis-operator/${VERSION}/config/crd/bases/redis.io_redisclusters.yaml"
kubectl apply -f "https://raw.githubusercontent.com/howl-cloud/redis-operator/${VERSION}/config/crd/bases/redis.io_redisbackups.yaml"
kubectl apply -f "https://raw.githubusercontent.com/howl-cloud/redis-operator/${VERSION}/config/crd/bases/redis.io_redisscheduledbackups.yaml"Install the chart:
helm upgrade --install redis-operator oci://ghcr.io/howl-cloud/charts/redis-operator \
--version "${VERSION#v}" \
--namespace redis-operator-system \
--create-namespaceOption C: Helm chart from local checkout.
Install CRDs first (same as above), then:
helm upgrade --install redis-operator ./charts/redis-operator \
--namespace redis-operator-system \
--create-namespaceapiVersion: redis.io/v1
kind: RedisCluster
metadata:
name: my-redis
namespace: default
spec:
instances: 3
mode: standalone
storage:
size: 1Gikubectl apply -f rediscluster.yaml
kubectl wait --for=condition=Ready rediscluster/my-redis -n default --timeout=10mPASSWORD=$(kubectl get secret my-redis-auth -n default -o jsonpath='{.data.password}' | base64 -d)
kubectl port-forward svc/my-redis-leader 6379:6379 -n default
redis-cli -a "$PASSWORD" -h 127.0.0.1 -p 6379 pingspec.storage.type selects how each data pod backs /data:
pvc(default) — a PersistentVolumeClaim per pod. Data survives pod recreation, rescheduling, and rolling updates.storage.sizeis the PVC request andstorage.storageClassNameselects the StorageClass. This is the right choice for any cluster that holds state you care about.emptyDir— pod-local ephemeral storage. Use this for caches and disposable queue backends where data loss on pod recreation is acceptable.storage.sizeis applied as the emptyDirsizeLimit(the pod is evicted if it exceeds it);storageClassNameis not allowed.
spec:
instances: 3
mode: standalone
storage:
type: emptyDir
size: 1GiData-loss semantics for emptyDir: an emptyDir volume lives and dies with its pod. Any event that recreates a pod — node drain or failure, eviction (including exceeding size), a rolling update, or a manual delete — permanently discards that pod's Redis data. In sentinel mode a recreated replica re-syncs from the current primary, but if all pods are lost at once (or the standalone pod is recreated) the dataset is gone. Because the data is disposable:
- PVCs are never created, and PVC resize, dangling-PVC accounting, and PVC health status do not apply.
storage.typeis immutable after creation (switching backends would destroy or migrate data).spec.bootstrap(restore-from-backup) is rejected — restored data would not survive the first pod recreation.
On-demand and scheduled backups still work for emptyDir clusters (they snapshot a live pod), but a backup only captures the data present at backup time.
A Kubernetes memory limit and Redis maxmemory are enforced by different layers. If a pod has spec.resources.limits.memory but Redis has no maxmemory, Redis keeps allocating until the kernel OOM-kills the container — an abrupt, unpredictable failure. Setting maxmemory makes Redis itself enforce a ceiling below the container limit, so it applies its eviction policy (or rejects writes) instead of being killed.
Configure this with first-class fields under spec.memory rather than raw spec.redis keys, so the operator can keep maxmemory consistent with the container limit and validate unsafe combinations:
spec:
resources:
limits:
memory: 1Gi
memory:
maxMemoryPercent: 75 # maxmemory = 75% of the container limit (768Mi)
maxMemoryPolicy: allkeys-lrumaxMemory— an explicit value (e.g.512Mi). Mutually exclusive withmaxMemoryPercent.maxMemoryPercent— derivesmaxmemoryas a percent (1–100) ofspec.resources.limits.memory. Requires that limit. Leave headroom (≈75%) for replication buffers, copy-on-write during persistence (BGSAVE/AOF rewrite), and fragmentation; using the full limit risks OOM despitemaxmemory.maxMemoryPolicy— the eviction policy. Defaults tonoeviction, which rejects writes at the limit instead of silently dropping data. Chooseallkeys-lru/allkeys-lfufor caches, or avolatile-*policy to evict only keys with a TTL.
Both fields are applied live (via CONFIG SET) and persisted to the rendered redis.conf. The webhook rejects setting maxmemory/maxmemory-policy in both spec.memory and spec.redis, and warns when a memory limit is set with no maxmemory configured. See docs/memory.md for the full interaction model.
RedisCluster: cluster lifecycle, topology, resources, secrets, scheduling rulesRedisBackup: one-off backup execution (S3-compatible or Azure Blob destinations)RedisScheduledBackup: cron-driven backup scheduling
Backup destinations, credential secret keys, and example manifests are documented in internal/controller/backup/README.md.
- Architecture and component docs:
internal/controller/cluster/README.md,internal/instance-manager/README.md - Memory and eviction:
docs/memory.md - Connection Secret:
docs/connection-secret.md - Monitoring and alerting:
docs/monitoring.md - Runbooks:
docs/runbooks/index.md - Upgrade guidance:
docs/upgrade.md - Contributor guide:
CONTRIBUTING.md
- Horizon Web Labs.