What Sentinel and Cluster actually do

Sentinel is a separate set of processes, usually three or more, that monitor a single primary and its replicas. When enough Sentinels agree the primary is unreachable (the quorum), one of them is elected to run the failover. It promotes a replica, repoints the other replicas at it, and returns the new address to any client that asks. The data layer is unchanged: one primary holds every key, and Sentinel only decides which node that is.

Cluster removes the external monitor and splits the data. The keyspace is divided into 16384 hash slots, and each key maps to a slot through a hash of its name. Slots are spread across several primaries, and each primary has its own replicas. The nodes gossip with each other to detect failures. When a majority of primaries agree a primary is down, one of its replicas promotes itself and takes over that primary's slots.

        SENTINEL                          CLUSTER

  [S1]   [S2]   [S3]          slots 0-5460  5461-10922  10923-16383
     \    |    /                  [P1]         [P2]          [P3]
      monitor                      |            |             |
         |                        [R1]         [R2]          [R3]
     [Primary] all keys
      /      \                    nodes gossip, detect failure,
  [Replica] [Replica]             and promote replicas themselves

Sentinel gives one dataset automatic failover. Cluster gives you sharding with failover built in. Both can survive the loss of a primary. They differ in how much data and write throughput each can handle. So the useful question is not which option is more available. It is whether a single primary can still hold your working set and absorb your write load. If it can, Cluster adds complexity you don't need.

      SENTINEL: one dataset       |       CLUSTER: 16384 hash slots
                                  |
 +----+    +----+    +----+       | +-----------+ +-----------+ +-----------+
 | S1 |    | S2 |    | S3 |       | |    P1     | |    P2     | |    P3     |
 +----+    +----+    +----+       | |  0-5460   | |5461-10922 | |10923-16383|
    \         |         /         | +-----------+ +-----------+ +-----------+
     \    monitor     /           |       |             |             |
      v       v      v            |       v             v             v
     +-----------------+          | +-----------+ +-----------+ +-----------+
     |     Primary     |          | |    R1     | |    R2     | |    R3     |
     |    all keys     |          | +-----------+ +-----------+ +-----------+
     +-----------------+          |
       |             |            | <-- nodes gossip with each other -->
       v             v            |
  +---------+   +---------+       | On failure: a majority of primaries
  | Replica |   | Replica |       | agree; a replica of the failed
  +---------+   +---------+       | primary promotes itself and takes
                                  | over that primary's slots.
 On failure: quorum agrees,       |
 one Sentinel promotes a          |
 replica, tells clients the       |
 new primary address              |

Sentinel adds external monitors and automatic failover to a single primary that holds every key, while Cluster spreads 16384 hash slots across several primaries whose nodes detect failures and promote replicas themselves.

How failover works and what to tune

Sentinel processes each ping the primary. When one gets no valid reply for down-after-milliseconds, it marks the primary subjectively down. Once quorum sentinels agree, the primary is objectively down, but a failover still needs a leader elected by a majority of all sentinels. That majority rule causes the common misconfigurations. With two sentinels, losing one blocks every failover. Placing a sentinel on the primary's host means one machine failure removes both. Spreading three sentinels so that a partition leaves no side with two means the quorum is configured but can never be reached. The safe pattern is at least three sentinels on independent failure domains.

Cluster nodes gossip with each other. A node unreachable for longer than cluster-node-timeout is flagged as possibly failed. It is confirmed failed only when a majority of primaries agree, and its replica needs votes from that same majority to be promoted. A minority partition cannot fail over. With cluster-require-full-coverage yes, the default, the whole cluster stops serving queries if any hash slot has no primary. For caches, no is often the better trade.

Replication is asynchronous in both topologies, so a write acknowledged by the primary can vanish if a replica that never received it is promoted. WAIT only confirms that replicas received the write. It does not stop failover from picking a different replica, and the write stays applied on the primary even when WAIT times out. During a partition, an isolated primary keeps accepting writes until it is demoted. min-replicas-to-write limits that split-brain window but makes the primary refuse writes whenever its replicas lag or disconnect.

Short timeouts give faster failover, but they also turn GC pauses, slow fsyncs and noisy neighbors into spurious promotions.

# ---- sentinel.conf (identical on 3 hosts in separate failure domains, none on the primary's host) ----
port 26379
# quorum = 2: two of three sentinels must agree the primary is down (ODOWN).
# The failover itself still needs a leader elected by a majority of all sentinels (2 of 3).
sentinel monitor cache-primary 10.0.1.10 6379 2
# No valid reply for 5s => SDOWN. Lower = faster failover, more false positives from GC/fsync stalls.
sentinel down-after-milliseconds cache-primary 5000
sentinel failover-timeout cache-primary 60000
# Resync replicas to the new primary one at a time
sentinel parallel-syncs cache-primary 1

# ---- redis.conf for a primary watched by Sentinel (optional split-brain limiter) ----
# Refuse writes unless >= 1 replica has acked within 10s.
# This shrinks the window where an isolated primary takes writes that are later lost,
# but the primary also rejects writes whenever its replicas lag or disconnect.
# min-replicas-to-write 1
# min-replicas-max-lag 10

# ---- redis.conf for each Redis Cluster node ----
port 6379
cluster-enabled yes
# Written and owned by Redis; do not edit by hand
cluster-config-file nodes-6379.conf
# Node unreachable this long => flagged PFAIL; becomes FAIL only when a majority of primaries agree
cluster-node-timeout 5000
# For a cache: keep serving the slots that are still covered instead of
# rejecting all queries when some slot has no reachable primary (default is yes)
cluster-require-full-coverage no

Minimal sentinel.conf and Redis Cluster redis.conf snippets showing quorum, down-after-milliseconds, cluster-node-timeout, cluster-require-full-coverage and the optional min-replicas-to-write split-brain trade-off.

How clients find the primary and what Cluster changes in your data model

With Sentinel, clients don't hardcode the primary's address. A Sentinel-aware client connects to the Sentinels, asks for the current primary by its configured service name, and reconnects there after a failover. A client pointed at a fixed IP keeps writing to the demoted node, which now rejects writes with READONLY errors until someone restarts the client.

Cluster splits the keyspace into 16,384 hash slots. Each key maps to a slot through CRC16 of the key, modulo 16,384, and the client caches the map of which node owns which slot. CLUSTER KEYSLOT user:42:profile shows where a key lands. If the client sends a command to the wrong node, it gets one of two replies:

  • MOVED: the slot has permanently moved. The client should refresh its slot map.
  • ASK: the slot is mid-migration. The client should send ASKING and retry this one command on the target node, without updating the map.

A client without cluster support connects to one node. Commands succeed only for keys whose slots that node happens to own, so failures depend on the key name and look random.

Multi-key operations such as MGET, MULTI/EXEC and Lua scripts must touch keys in a single slot. Lua scripts must also declare their keys in KEYS. A command like MGET user:42:profile user:42:cart usually fails with CROSSSLOT Keys in request don't hash to the same slot.

Hash tags fix this. Only the text inside the first {} is hashed, so {user:42}:profile and {user:42}:cart share a slot. The trade-off is load: one heavily used tag concentrates traffic on a single slot, and a single slot always lives on one node.

Cluster also supports only database 0, and SELECT with any other index returns an error. Applications that separate data by database number need key prefixes instead.

# Connect in cluster mode (-c) so redis-cli follows MOVED/ASK redirections.
# Host and port are placeholders for any node in the cluster.
redis-cli -c -h 10.0.0.11 -p 6379

# Ask which of the 16,384 slots each key hashes to.
# The two keys generally land in different slots.
> CLUSTER KEYSLOT user:42:profile
> CLUSTER KEYSLOT user:42:cart

# A multi-key command across slots is rejected, even with -c:
> MGET user:42:profile user:42:cart
(error) CROSSSLOT Keys in request don't hash to the same slot

# Hash tags: only the text inside the first {} is hashed,
# so both keys map to the same slot as the tag "user:42".
> CLUSTER KEYSLOT {user:42}:profile
> CLUSTER KEYSLOT {user:42}:cart
# Both commands return the same slot number.

> SET {user:42}:profile '{"name":"Ada"}'
> SET {user:42}:cart '["sku-1001"]'
> MGET {user:42}:profile {user:42}:cart   # succeeds: one slot, one node

# Without -c, a plain client does not follow redirects. It gets an error
# for any key whose slot lives on another node:
# (error) MOVED <slot> <host>:<port>

# Cluster mode supports only database 0, so SELECT 1 fails.
# Use key prefixes such as sessions:... or cache:... instead.
> SELECT 1
(error) ERR SELECT is not allowed in cluster mode

A redis-cli session against a Redis Cluster that uses CLUSTER KEYSLOT, triggers a CROSSSLOT error with MGET, fixes it with {user:42} hash tags, and shows the MOVED and SELECT errors a client without cluster support will hit.

When to use Sentinel, Cluster, or neither

Sentinel fits when the dataset and write load fit on one primary and you want automatic failover without changing your data model. Cluster fits when memory or CPU outgrows one primary. Neither may be right for a pure cache: if losing a node only causes a temporary spike in misses, a standalone instance can be enough.

Illustrative estimate

These inputs are assumed, not measured:

  • Working set: 40 GB, growing.
  • Node RAM: 64 GB. A BGSAVE or full replica sync forks the process, and copy-on-write can duplicate pages under heavy writes. Budget data at no more than half of RAM, which gives 32 GB per primary.
  • Peak load: 250,000 ops/sec. Assume one primary sustains about 100,000 ops/sec for this command mix.

The 40 GB working set exceeds the 32 GB budget. Redis executes commands on a single thread, and replicas don't take writes, so 250k ops/sec needs at least three primaries. Three shards of about 13–14 GB each pass both limits and leave room to grow. At 25 GB and 80k ops/sec, Sentinel would still be enough.

Decision checklist

Question Yes No
Does the working set fit on one node with fork headroom? Sentinel or neither Cluster
Is one primary CPU-bound? Cluster (replicas help only for stale-tolerant reads) Sentinel or neither
Do you depend on multi-key operations across unrelated keys? Sentinel, or redesign with hash tags Cluster
Does your client library handle Cluster redirects? Cluster is viable Sentinel
Can the team operate a sharded topology? Cluster Sentinel or neither

When to avoid each, and the alternatives for pure caches

Avoid Cluster when

  • Your logic spans many keys. Multi-key commands, MULTI/EXEC transactions, and Lua scripts only work when every key involved hashes to the same slot. Otherwise Redis returns a CROSSSLOT error. Hash tags can fix a few access patterns, but putting too many keys under one tag recreates a hot shard.
  • You rely on SELECT. Cluster supports only database 0. Code that separates tenants or environments by database number must move that separation into key prefixes.
  • Your client support is weak. A client that does not follow MOVED/ASK redirects or refresh its slot map will fail during resharding and failover.

Avoid Sentinel when

  • The working set or write load exceeds one primary. Sentinel automates failover but does not shard. Every write still goes to one node, so its memory, CPU, and network capacity set the limit. Read replicas do not raise the write ceiling.

Alternatives for pure caches

Client-side consistent hashing spreads keys across independent instances that do not know about each other. If a node fails, its share of keys becomes misses until the client stops routing to it. There is no replication and no cross-node atomicity.

A single primary with a replica and manual failover fits a cache that is cheap to rebuild. It gives up availability during an incident. Writes stop until an operator promotes the replica and points clients at it.

Option Scales writes Automatic failover Multi-key ops Operational load
Sentinel No Yes Yes Moderate
Cluster Yes Yes Same slot only High
Client-side hashing Yes No (keys miss) Same node only Low–moderate
Primary + replica, manual No No Yes Low

Day-to-day operations and the stampede risk

Adding capacity

In Cluster, you add a node and move hash slots to it with redis-cli --cluster reshard or rebalance. Keys migrate while the cluster keeps serving traffic. Clients briefly receive MOVED and ASK redirects, so a smart client absorbs the change. With Sentinel, you have two options. You can scale vertically by adding a larger replica, promoting it, and retiring the old primary. Or you can split the keyspace by hand across a second Sentinel-managed group, which means changing application routing.

Monitoring and failover drills

Alert on replication lag, connected_slaves, and, in Cluster, cluster_state and slot coverage. Then trigger failover on purpose in staging. Use SENTINEL FAILOVER <master-name> for Sentinel, or run CLUSTER FAILOVER on a replica for Cluster. A drill shows how long clients really take to reconnect, which is often longer than the server-side switchover.

Announced addresses

Nodes advertise their own IP and port to clients and peers. Behind NAT, in containers, or on Kubernetes, the address a node sees for itself is often unreachable from outside. Set cluster-announce-ip, replica-announce-ip, or Sentinel's announce-ip to an address clients can reach. If you skip this, failover "succeeds" but clients get redirected to addresses they cannot connect to.

The stampede

After a failover, replication lag may have lost recent writes. During a reshard, redirects add latency. Either event can trigger a burst of misses that lands on your database all at once. Three mitigations help:

  • Request coalescing: one caller rebuilds a key while the others wait for that result.
  • TTL jitter: randomized expiry stops keys from expiring together.
  • Rate limiting on backend fills.

A stampede can take down your database regardless of topology, so it often deserves more attention than the Sentinel-versus-Cluster decision itself.

import random, threading, time, uuid
import redis

r = redis.Redis(host="cache.internal.example", port=6379)
BASE_TTL = 300  # seconds
# Rate limit: cap concurrent backend fills from this process
db_fill_limit = threading.BoundedSemaphore(8)
# Release the lock only if we still own it (compare token, then delete)
RELEASE = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) end return 0"

def get_or_rebuild(key, load_from_db, wait_s=2.0):
    val = r.get(key)
    if val is not None:
        return val
    lock_key, token = f"lock:{key}", uuid.uuid4().hex
    # Request coalescing: only the caller that wins the lock rebuilds the key
    if r.set(lock_key, token, nx=True, px=5000):
        try:
            with db_fill_limit:
                val = load_from_db(key)
            # TTL jitter: +/-10% so keys refilled together don't expire together
            r.set(key, val, ex=int(BASE_TTL * random.uniform(0.9, 1.1)))
            return val
        finally:
            r.eval(RELEASE, 1, lock_key, token)
    # Losers poll briefly for the winner's result instead of hitting the DB
    deadline = time.monotonic() + wait_s
    while time.monotonic() < deadline:
        time.sleep(0.05)
        val = r.get(key)
        if val is not None:
            return val
    # Fallback after timeout still goes through the fill limit
    with db_fill_limit:
        return load_from_db(key)

A redis-py cache-aside helper that blunts a post-failover or post-reshard miss storm by combining a SET NX lock for request coalescing, randomized TTLs, and a semaphore that caps concurrent database fills.

Impact on cost and engineering time

Every replica holds a full copy of the data it replicates, so replica count multiplies memory rather than adding capacity. Sentinel adds monitoring processes but no data copies. Cluster adds primaries, and each one usually needs its own replica.

Sentinel Cluster
Minimum data nodes for HA 1 primary + 1 replica 3 primaries + 3 replicas
Extra processes 3+ Sentinels (small, can share hosts) None, but a cluster bus between nodes
Client requirement Sentinel-aware discovery Slot routing, MOVED/ASK handling
Data-model constraints None Multi-key ops need one hash slot; database 0 only

Illustrative estimate (assumed numbers, not a benchmark)

Provisioned memory = dataset × (1 + replicas per primary) × headroom

Assume a 30 GB dataset, one replica per primary, and 1.3× headroom for fragmentation and fork copy-on-write: 30 × 2 × 1.3 = 78 GB. Sentinel spreads that over 2 data nodes. A three-shard Cluster spreads the same 78 GB over 6 smaller nodes. The total memory is equal, but Cluster has three times as many nodes to patch, monitor and alert on.

Cluster's development cost comes from the data model. You need hash tags for multi-key commands, Lua scripts must touch keys in a single slot, and pipelines get split by slot.

Moving from Sentinel to Cluster takes three pieces of work:

  • Client changes: switch to a cluster-aware client.
  • Key schema review: find cross-slot operations and add hash tags where needed.
  • Warm-up plan: for a cache, the new cluster usually starts empty, so shift traffic gradually instead of copying data.

Pick the simplest topology that fits your memory and throughput needs. Design the application to treat a miss as a slower read, not an error. That way you can skip persistence tuning and backups, which a cache does not need.

How OneEnv can help

Disclosure: OneEnv is the product behind this blog.

OneEnv (oneenv.io) is a workspace-based governance platform for the things services share — environment variables, database structure, API routes and validation rules. A change to a shared value is staged on a branch, reviewed once, and then every affected service explicitly approves or rejects it before it goes live, so you can see who a change touches before it ships instead of finding out in production.

Applied to redis cluster vs sentinel, this is where it helps:

Saves time

  • Every service that inherits a shared value is listed in one place, so nobody searches repos and chats to find out who is affected.
  • One review per branch replaces coordinating each team separately; each service still keeps its own approve/reject.
  • Changing a shared variable is one edit and one review instead of editing every service's .env by hand.

Saves cost

  • Fewer production incidents and rollbacks caused by a shared value, route or column changing under a consumer unannounced.
  • Less duplicated configuration to maintain: one shared definition inherited by many services.
  • A wrong or stale value is caught at review time by the services it affects, not by a failed deploy.
  • OneEnv has a free plan (1 workspace, unlimited services and members, full governance), so trying it costs nothing.

These are qualitative benefits, not measured results — how much you save depends on how many services share the value.

Want to try it? Start at oneenv.io.