Connecting Redisson to a Valkey or Redis Cluster on Kubernetes

Published on
Jun 22, 2026

Running a sharded Valkey or Redis cluster on Kubernetes is now a common practice for enterprise applications. However, this can be tricky for Java development teams due to how Kubernetes and distributed in-memory datastores handle networking. Valkey/Redis clusters use the gossip protocol to manage node addresses among members, and clients follow MOVED/ASK redirects to the node that owns a key's hash slot. On Kubernetes, those addresses are ephemeral Pod IPs, which complicates discovery and failover.

Redisson, the Java client for Valkey and Redis, provides a straightforward solution for developers by seamlessly abstracting the complexities of managing clusters. Instead of forcing developers to manage byte arrays and command execution, Redisson provides distributed, thread-safe Java objects (like ConcurrentHashMap, Lock, and Queue) backed by the cluster. When operating in a volatile Kubernetes environment, Redisson's built-in connection pooling, automatic topology discovery, and intelligent retry mechanisms act as a crucial stability layer between your application and the database.

This guide will walk you through everything you need to connect Redisson to a Valkey or Redis cluster on Kubernetes, from deploying the cluster and configuring the client to discovering the node topology and cleanly surviving failovers.

Choosing a Kubernetes Deployment Strategy: Operators, Helm, or StatefulSets

The first step is selecting your Kubernetes deployment strategy. There are three viable methods to choose from, and the right one depends on how much Day-2 automation you want.

The StatefulSet

A raw StatefulSet gives you full control over a cluster with zero dependencies. You simply define the Pods, a headless Service for stable DNS, and bootstrap the cluster with the command valkey-cli --cluster create.

This approach is transparent and reproducible. On the other hand, it also means your Java developers own resharding, failover recovery, scaling, and backups. Therefore, a StatefulSet might be the best option for learning or for small-footprint clusters.

Helm Charts

A Helm chart packages the StatefulSet — plus services, config, and probes — into a versioned, parameterized release. You get repeatable installs and easy upgrades, but a chart only deploys a database; it doesn't operate one. No controller is watching for failed primaries or drifting slot assignments.

If you previously deployed the Bitnami Valkey chart, you're likely aware of the changes that took effect in late August 2025. Bitnami moved its maintained container images behind a commercial subscription (Bitnami Secure Images) and archived the old free images to a separate, frozen bitnamilegacy namespace. Pipelines that pulled oci://registry-1.docker.io/bitnamicharts/... during deploys can now hit ImagePullBackOff or auth errors.

In response, the Valkey project shipped an official, community-maintained Helm chart at valkey-io/valkey-helm. It lets you pin chart and image versions in values.yaml and upgrade on your own schedule, free of vendor policy changes. Just be aware that this chart focuses on the basics, with support for standalone and primary–replica topologies, rather than a sharded cluster. So if you need Redisson's cluster mode (sharded, multi-primary with hash slots), you'll get there with an operator, the Bitnami Secure Images valkey-cluster chart, or a StatefulSet.

Here's a shell script to get you started with valkey-helm:

helm repo add valkey https://valkey.io/valkey-helm/
helm repo update
helm install valkey valkey/valkey -n data --create-namespace

Kubernetes Operators

In a full-scale production environment, Kubernetes Operators are the more compelling option.

With this option, an operator runs a controller that builds your specified cluster and automatically handles failover, online scaling, slot rebalancing, backup/restore, and rolling config changes.

KubeBlocks, an open-source operator from ApeCloud, is one of the more popular deployments. KubeBlocks manages Valkey or Redis through a unified Cluster CRD and an InstanceSet workload that tracks each Pod's role. It also supports true sharded topologies with OpsRequest-driven Day-2 operations. Other options include the Hyperspike Valkey operator. Operators add a control-plane component to learn and maintain, but for production clusters that scale or need to handle failovers, they pay for themselves.

Building a Valkey Cluster via StatefulSet

The best way to understand how clients interact with cluster topology is to build one from the ground up. The YAML manifest below creates a cluster with six nodes (three primaries, three replicas) via a headless service. Please note, however, that this demo runs without authentication. It disables protected mode, allowing peers and clients to connect from the outside loopback interface. In a production environment, you would want to set a password or ACLs.

With that in mind, here's the manifest:

apiVersion: v1
kind: ConfigMap
metadata: { name: valkey-conf, namespace: data }
data:
  valkey.conf: |
    cluster-enabled yes
    cluster-config-file /data/nodes.conf
    cluster-node-timeout 5000
    cluster-preferred-endpoint-type hostname
    appendonly yes
    dir /data                  # AOF/RDB persist on the mounted PVC
    # No auth in this demo, so protected mode must be off or peers/clients
    # connecting from non-loopback are refused. In production set a password
    # (requirepass/ACLs) instead — that disables protected mode too.
    protected-mode no
---
apiVersion: v1
kind: Service
metadata: { name: valkey-headless, namespace: data }
spec:
  clusterIP: None            # headless: each Pod gets a stable DNS name
  selector: { app: valkey }
  ports: [{ port: 6379, name: client }, { port: 16379, name: gossip }]
---
apiVersion: apps/v1
kind: StatefulSet
metadata: { name: valkey, namespace: data }
spec:
  serviceName: valkey-headless
  replicas: 6
  selector: { matchLabels: { app: valkey } }
  template:
    metadata: { labels: { app: valkey } }
    spec:
      containers:
      - name: valkey
        image: valkey/valkey:8.1
        env:
        - name: POD_NAME
          valueFrom: { fieldRef: { fieldPath: metadata.name } }
        - name: NAMESPACE
          valueFrom: { fieldRef: { fieldPath: metadata.namespace } }
        command: ["sh", "-c"]
        args:                       # each Pod announces its OWN stable FQDN
        - |
          exec valkey-server /conf/valkey.conf \
          --cluster-announce-hostname \
          "${POD_NAME}.valkey-headless.${NAMESPACE}.svc.cluster.local"
        ports: [{ containerPort: 6379 }, { containerPort: 16379 }]
        volumeMounts:
        - { name: conf, mountPath: /conf }
        - { name: data, mountPath: /data }
      volumes:
      - { name: conf, configMap: { name: valkey-conf } }
  volumeClaimTemplates:
  - metadata: { name: data }
    spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: 8Gi } } }

In this demo, the Kubernetes hostname configuration is perhaps the most important detail because it depends on two settings working together. In the ConfigMap, cluster-preferred-endpoint-type hostname tells nodes to return DNS names rather than ephemeral Pod IPs in CLUSTER SLOTS and MOVED/ASK redirects. Since a node on its own has no hostname to provide and advertise, these settings provide stable DNS for the cluster.

So each Pod must also set cluster-announce-hostname to its own FQDN; since that value differs per Pod, it's built from the Downward API in the container args (e.g., valkey-0.valkey-headless.data.svc.cluster.local). With both in place, the topology stays valid across restarts and rescheduling.

Once all six Pods are running, bootstrap the cluster with this shell script:

PODS=$(for i in 0 1 2 3 4 5; do
  echo "valkey-$i.valkey-headless.data.svc.cluster.local:6379"
done)

kubectl exec -n data valkey-0 -- valkey-cli --cluster create $PODS \
  --cluster-replicas 1 --cluster-yes

Streamlining Operations: Managing Clusters with Kubernetes Operators

While the StatefulSet is great for understanding the mechanics of a cluster and its topology, Kubernetes Operators provide a production-grade experience. In particular, an operator removes the manual bootstrap and the Day-2 toil.

With KubeBlocks installed, the entire three-shard cluster is simply a single resource:

apiVersion: apps.kubeblocks.io/v1
kind: Cluster
metadata: { name: redis-sharding, namespace: data }
spec:
  terminationPolicy: Delete
  shardings:
  - name: shard
    shards: 3                        # >= 3 primaries; reshard by changing this number
    template:
      name: redis
      componentDef: redis-cluster-7
      serviceVersion: 7.2.4
      replicas: 2                    # one primary + one replica per shard
      volumeClaimTemplates:
        - name: data
          spec:
            accessModes: ["ReadWriteOnce"]
            resources: { requests: { storage: 20Gi } }
  services:
  - name: redis-advertised     # per-Pod service that advertises reachable endpoints
    podService: true
    serviceType: NodePort       # NodePort or LoadBalancer for reach outside the cluster

KubeBlocks takes care of most of the hard work. It provisions the Pods, forms the cluster, assigns the hash slots, and labels each Pod's role (primary or secondary). You don't even have to issue the valkey-cli --cluster create command. Resharding is declarative: raise shards (or submit a HorizontalScaling OpsRequest) and the operator rebalances slots online. KubeBlocks ships this as a Redis cluster addon, and Redisson connects to it exactly as it would to the Valkey cluster above.

Configuring Redisson for Kubernetes Cluster Discovery

Once the backend infrastructure is running, the next step is to connect your Java application, and this is where Redisson comes into the picture.

First, add the dependency (org.redisson:redisson:4.4.0 or newer), then configure cluster mode. You only need to seed Redisson with a couple of stable node addresses, and it will automatically discover the rest of the topology. Redisson will also periodically re-scan the topology for changes.

You now have a couple of choices for in-cluster configuration: Java or YAML. Here's the Java code:

Config config = new Config();
config.useClusterServers()
    // Seed with stable headless DNS names — not Pod IPs
    .addNodeAddress(
        "redis://valkey-0.valkey-headless.data.svc.cluster.local:6379",
        "redis://valkey-1.valkey-headless.data.svc.cluster.local:6379")
    .setScanInterval(2000)          // re-scan topology every 2s
    .setReadMode(ReadMode.SLAVE)    // read from replicas, write to primaries
    .setSubscriptionMode(SubscriptionMode.SLAVE);

RedissonClient redisson = Redisson.create(config);

And here's the equivalent YAML:

clusterServersConfig:
  nodeAddresses:
    - "redis://valkey-0.valkey-headless.data.svc.cluster.local:6379"
    - "redis://valkey-1.valkey-headless.data.svc.cluster.local:6379"
  scanInterval: 2000
  readMode: "SLAVE"
  subscriptionMode: "SLAVE"

Out-of-Cluster Reachability and NAT Mapping

When Redisson is running outside Kubernetes — for example, on a developer laptop, in a different cluster, or behind a NAT — the internal addresses that the nodes gossip won't resolve. So, in general, you will always prefer running the Redisson client in-cluster, but there are two ways to make an in-cluster sharded cluster reachable from the outside, either server-side or client-side.

Server-side: Have each node advertise an externally routable address. KubeBlocks' redis-advertised NodePort/LoadBalancer service can do this, so the topology a client discovers already points to reachable endpoints.

Client-side: When the cluster only knows its internal addresses, Redisson's natMapper rewrites them for outside access:

HostPortNatMapper natMapper = new HostPortNatMapper();
natMapper.setHostsPortMap(Map.of(
    // internal address (as gossiped) -> externally reachable address
    "valkey-0.valkey-headless.data.svc.cluster.local:6379", "node1.example.com:31000",
    "valkey-1.valkey-headless.data.svc.cluster.local:6379", "node2.example.com:31001"
    // ...one entry per node — primaries and replicas alike
));

Config config = new Config();
config.useClusterServers()
    .addNodeAddress("redis://node1.example.com:31000")   // an externally reachable seed
    .setNatMapper(natMapper);

The map needs an entry for every node, because Redisson applies it to each address returned by CLUSTER SLOTS. A node will be unreachable if there is no entry for it.

Use HostPortNatMapper when the external port differs (NodePort) and HostNatMapper when only the host changes (i.e., as with a per-node LoadBalancer).

Optimizing Redisson Connection Pooling and Failover Mechanisms

Redisson maintains separate connection pools per node, split between primaries and replicas. Its defaults include minimum idle sizes of around 24 and pool sizes of around 64 per node, which, when multiplied across a six-node cluster and several application replicas, can produce connection storms against a fresh cluster.

Here's a connection pooling configuration that makes a good starting point for most clusters:

masterConnectionMinimumIdleSize: 4
masterConnectionPoolSize: 32
slaveConnectionMinimumIdleSize: 4
slaveConnectionPoolSize: 32
subscriptionConnectionPoolSize: 25

For failovers, Redisson retries failed commands and reconnects with a backoff delay. It spreads reconnect attempts so a primary failover doesn't trigger a surge in connections to one node:

retryAttempts: 4
retryDelay: ! { baseDelay: PT1S, maxDelay: PT2S }
reconnectionDelay: ! { baseDelay: PT0.1S, maxDelay: PT10S }
timeout: 3000
connectTimeout: 10000

When a primary fails, the cluster promotes a replica; Redisson's next topology scan (scanInterval) picks up the new primary and routes writes accordingly. Keeping scanInterval modest (say, 1 to 2 seconds) shortens the window where stale routing causes errors, at the cost of a little extra discovery traffic. Pair this with cluster-node-timeout on the server side, so the cluster will always fail over promptly.

Implementing Robust Kubernetes Health Checks for Valkey and Redis

Once your cluster is up and running, you will want to run periodic health checks to ensure nodes are responding and the topology is stable. The combination of Kubernetes container probes and Redisson connection validations ensures that everything is running well on both the cluster and client sides.

Kubernetes Probes

On the Kubernetes side, you can perform liveness, readiness, and startup probes. The StatefulSet will then replace dead nodes:

startupProbe:                     # tolerate slow AOF/RDB loads on restart
  exec: { command: ["sh", "-c", "valkey-cli ping | grep -q PONG"] }
  periodSeconds: 5
  failureThreshold: 60            # ~5 min before the other probes engage
readinessProbe:                   # keep still-loading nodes out of rotation
  exec: { command: ["sh", "-c", "valkey-cli ping | grep -q PONG"] }
  periodSeconds: 10
livenessProbe:                    # conservative TCP check, no mid-failover kills
  tcpSocket: { port: 6379 }
  periodSeconds: 15

Amongst the shell commands embedded here, grep PONG is perhaps the most important. A node still replaying its AOF answers -LOADING and an auth-enabled node answers NOAUTH, and both should be seen as not ready instead of falsely passing an exit-code check. The startupProbe allows the replay time to finish before the liveness and readiness probes begin. This ensures that a slow-loading node isn't killed mid-load, and a quick TCP check helps avoid restarting a node mid-failover. If you enabled authentication, pass the appropriate credentials to the probes by setting the REDISCLI_AUTH environment variable; this allows valkey-cli to authenticate.

Redisson Client-Side Connection Validation

On the client side, Redisson performs its own health checks on its pooled connections. You only need to set pingConnectionInterval so that idle connections are validated, and silently broken ones are detected and replaced. Here's the YAML to perform these checks:

pingConnectionInterval: 30000   # ping each pooled connection every 30s to spot dead ones

The combination of Kubernetes-side and client-side checks will keep a Redisson client healthy throughout a cluster.

Cluster Management in Java and More

Valkey and Redis are key components of today's enterprise distributed applications, thanks to their speed and versatility. Managing a cluster with manual Java code can easily become overwhelming, but Redisson abstracts complex concepts into familiar objects and intuitive APIs.