What Sentinel and Cluster actually do

Redis Sentinel and Redis Cluster are often framed as "basic HA" versus "advanced HA". That framing is misleading, because they solve different problems.

Sentinel is a separate set of monitoring processes. A group of Sentinels watches one primary and its replicas. When enough of them agree the primary is unreachable, they promote a replica and reconfigure the others to follow it. Clients do not hard-code the primary's address. They ask Sentinel where the primary is. All data still lives on a single primary.

Cluster is a sharded topology built into Redis itself. The keyspace is split across several primaries, and each primary has its own replicas. The nodes talk to each other over a dedicated cluster bus and handle failover themselves, with no external monitor.

SENTINEL                          CLUSTER

 [S1]   [S2]   [S3]               [P1]<====>[P2]<====>[P3]
   \     |     /                   |  \\      ||      //  |
    watch & vote                   |   \\==== bus ====//  |
         |                        [R1]      [R2]      [R3]
     [Primary]
      /     \                     each shard: one primary,
 [Replica] [Replica]              one replica; nodes gossip
                                  and fail over among themselves

Sentinel answers the question "how do I survive losing my primary?" Cluster answers the question "how do I spread data and writes across several primaries?" Neither is simply more available than the other.

Sentinel fits teams whose dataset and write rate fit comfortably on one node and who need automatic failover. Cluster fits teams whose memory footprint or write throughput exceeds what a single primary can handle.

SENTINEL: one primary, 3 monitors   CLUSTER: 3 shards, no external monitor

+----+   +----+   +----+            +---------+    +---------+    +---------+
| S1 |   | S2 |   | S3 |            |   P1    |<==>|   P2    |<==>|   P3    |
+----+   +----+   +----+            | slots A |    | slots B |    | slots C |
  |        |        |               +---------+    +---------+    +---------+
  +--------+--------+                    |              |              |
           | watch, vote,                v              v              v
           v fail over              +---------+    +---------+    +---------+
     +-----------+                  |   R1    |    |   R2    |    |   R3    |
     |  Primary  | all data         +---------+    +---------+    +---------+
     +-----------+                       |              |              |
       |           |                =====+==============+==============+=====
       v           v                 cluster bus: all six nodes gossip,
  +---------+ +---------+            detect failure, and promote a
  | Replica | | Replica |            replica with no outside monitor
  +---------+ +---------+

Clients ask Sentinel for the        Clients route keys by hash slot.
primary's address.

Left: three Sentinel processes watch a single primary and its replicas and fail over when they agree it is down. Right: a three-shard Redis Cluster where each primary owns part of the keyspace, has its own replica, and all nodes coordinate failover over the cluster bus.

How redis failover works in each mode

Sentinel

Each Sentinel pings the primary. If it gets no valid reply for down-after-milliseconds, that Sentinel marks the primary subjectively down (SDOWN). This is one observer's opinion. The primary becomes objectively down (ODOWN) only when the number of Sentinels set by quorum in sentinel monitor agree. Quorum controls detection only. To actually run a failover, a Sentinel must be elected leader by a majority of all Sentinels.

The leader then picks a replica. It considers replica priority, then replication offset, which favors the most up-to-date replica. It promotes that replica and repoints the others to it. parallel-syncs caps how many replicas resync at the same time, because a resyncing replica may not be able to serve reads. failover-timeout limits how long an attempt can run before another one is allowed.

Run an odd number of Sentinels, at least three. Four Sentinels survive the same number of losses as three, so the fourth adds no fault tolerance. Place them in independent failure domains. If Sentinels share hosts or a zone with the Redis nodes they monitor, one outage can remove the primary and a majority of voters together, and then no failover can happen.

Cluster

Nodes exchange heartbeats over the cluster bus. A node that misses heartbeats for longer than cluster-node-timeout is flagged PFAIL. It becomes FAIL when a majority of primaries report it as unreachable. A replica of the failed shard then asks the primaries for votes. If a majority approve, the replica takes over the shard's hash slots.

Timeouts are a trade-off

Short timeouts react faster but turn GC pauses or network blips into false failovers. Long timeouts avoid false failovers, but writes to the affected shard or primary stay unavailable for longer.

# ---- sentinel.conf (same file on each of 3+ Sentinels, separate failure domains) ----
port 26379
# Watch primary "mymaster" at 10.0.1.10:6379. The final 2 is the quorum:
# Sentinels that must agree before SDOWN becomes ODOWN. Electing a failover
# leader still needs a majority of all Sentinels.
sentinel monitor mymaster 10.0.1.10 6379 2
# No valid reply for 5s -> this Sentinel marks the primary SDOWN.
# Lower = faster detection but more false failovers on GC pauses or network blips.
sentinel down-after-milliseconds mymaster 5000
# Upper bound for a failover attempt before another may be tried.
sentinel failover-timeout mymaster 60000
# Resync replicas to the new primary one at a time so the rest keep serving reads.
sentinel parallel-syncs mymaster 1

# ---- redis.conf for each Cluster node ----
port 6379
# Run this instance in Cluster mode. The cluster bus uses port + 10000 (16379).
cluster-enabled yes
# Cluster state file, written and maintained by Redis. Do not edit by hand.
cluster-config-file nodes-6379.conf
# Heartbeats missing longer than this -> node flagged PFAIL. It becomes FAIL
# once a majority of primaries agree. Same trade-off as down-after-milliseconds.
cluster-node-timeout 15000
# Cluster persistence and failover rely on the AOF/RDB setup you already run.
appendonly yes

A minimal sentinel.conf with quorum, detection timeout, failover timeout and parallel-syncs, followed by the matching Redis Cluster node settings, with comments mapping each directive to the failover steps above.

How Cluster sharding works and what it costs your data model

Cluster divides the keyspace into 16,384 hash slots. Each key maps to the slot CRC16(key) mod 16384, and each primary owns a subset of slots. With a hash tag, only the substring between the first { and the next } is hashed, provided that substring is non-empty. Running CLUSTER KEYSLOT user:42:cart and CLUSTER KEYSLOT user:42:profile usually returns two different slots. Running CLUSTER KEYSLOT {user:42}:cart and CLUSTER KEYSLOT {user:42}:profile returns the same slot.

Redirections

  • MOVED means the slot now permanently lives on another node. The client should update its slot map and retry there.
  • ASK appears while a slot is being migrated. It means "this key has already moved, try the target once." The client sends ASKING to the target node, then repeats the command there. It must not update its slot map, because the slot still officially belongs to the source node.

Resharding

Slots move between nodes key by key while the cluster keeps serving traffic. Migrating a large key blocks both the source and the target during the transfer. During migration, ASK redirects add round trips for any traffic that touches the moving slot. Under heavy load, both effects show up as latency spikes, so schedule migrations off-peak and find your large keys first.

Multi-key constraints

Multi-key commands, MULTI/EXEC transactions, and Lua scripts must touch keys that all hash to the same slot. A command like MGET user:1 user:2 fails with CROSSSLOT Keys in request don't hash to the same slot. Hash tags fix this, but every key that shares a tag lands on one node, so a popular tag becomes a hot slot that you cannot spread out by adding nodes.

Cluster supports only database 0. If your application separates data with SELECT, you will need key prefixes instead. Adding hash tags to an existing keyspace changes the key names themselves. That means rewriting the application code that builds keys and migrating the existing data.

# Connect to any primary; -c makes redis-cli follow MOVED/ASK redirects.
RC="redis-cli -c -h 10.0.0.11 -p 7000"

# Without a hash tag the whole key is hashed, so these usually land in different slots.
$RC CLUSTER KEYSLOT user:42:cart
$RC CLUSTER KEYSLOT user:42:profile

# With a hash tag only "user:42" (between { and }) is hashed, so both return the same slot.
$RC CLUSTER KEYSLOT '{user:42}:cart'
$RC CLUSTER KEYSLOT '{user:42}:profile'

# An empty tag is ignored and the whole key is hashed.
$RC CLUSTER KEYSLOT '{}user:42:cart'

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

# The same keys with a shared tag are in one slot, so this works.
$RC SET '{user:42}:cart' 'sku-1001'
$RC SET '{user:42}:profile' 'name=Ada'
$RC MGET '{user:42}:cart' '{user:42}:profile'

# Count the keys in one slot to check whether a tag is getting hot.
# Run it on the node that owns the slot. Replace 1234 with a slot from CLUSTER KEYSLOT.
$RC CLUSTER COUNTKEYSINSLOT 1234

# Cluster mode supports only database 0, so SELECT 1 fails.
$RC SELECT 1

These redis-cli commands show how hash tags change the result of CLUSTER KEYSLOT, how a cross-slot MGET fails with CROSSSLOT while tagged keys succeed, how to check key counts in a slot, and that SELECT is unavailable in cluster mode.

Client requirements and common failure modes

The topology you choose decides what your client library has to do, and most Redis HA incidents trace back to the client rather than the server.

A Sentinel-aware client is configured with a set of Sentinel addresses and a master name, for example sentinels: [s1:26379, s2:26379, s3:26379] plus master: orders. It asks any Sentinel for the current primary, connects to that address, and asks again when the connection fails or a write returns READONLY, which is what a demoted primary sends back. A Cluster-aware client is configured with a few seed nodes, such as seeds: [n1:6379, n2:6379]. It uses them only to fetch the slot map, then routes each key to the node that owns its slot. On MOVED it updates the map. On ASK it retries once against the target node without updating the map, because the slot is still migrating.

Where incidents come from

  • Stale primary addresses. A client that resolves the primary once at startup keeps writing to a node that is now a replica.
  • Weak redirection handling. A client that treats MOVED as an error, or never refreshes its slot map, fails during resharding and failover.
  • Pool churn. Many connections drop and reconnect at the same moment, so retries pile onto the new primary.
  • Unreachable advertised addresses. Behind NAT or container networking, nodes report internal IPs to clients. Announce-ip and announce-port settings let each node advertise an address that clients can actually reach.

This is where the engineering time goes: client configuration, retry and timeout budgets that tolerate a failover window, and tests that kill a primary while the application is under load.

# Generic client configuration (not tied to any specific library)

# Sentinel mode: discover the primary through Sentinel
redis:
  mode: sentinel
  sentinels: [s1:26379, s2:26379, s3:26379]  # ask any of these
  master_name: orders                         # logical name, not an address
  connect_timeout_ms: 500
  retry: { attempts: 5, backoff_ms: 200, jitter: true }  # cover the failover window

# Cluster mode: seed nodes are only used to fetch the slot map
redis:
  mode: cluster
  seeds: [n1:6379, n2:6379, n3:6379]
  refresh_slot_map_on: [MOVED, connection_error]
  retry: { attempts: 5, backoff_ms: 200, jitter: true }

# Pseudocode: how each client reacts to failures
on_sentinel_error(err):
  if err is connection_lost or reply starts with "READONLY":
    primary = ask_any_sentinel("get primary address for", master_name)
    reconnect_pool(primary)          # drop connections to the old primary
    retry_with_backoff_and_jitter()  # avoid every client stampeding at once

on_cluster_reply(reply, cmd):
  if reply is "MOVED <slot> <host:port>":
    slot_map[slot] = host:port       # ownership changed permanently
    resend(cmd, host:port)
  else if reply is "ASK <slot> <host:port>":
    send(host:port, "ASKING")        # one-off redirect, slot is migrating
    resend(cmd, host:port)           # do NOT update slot_map

Pseudocode contrasting a Sentinel client configured with Sentinel addresses and a master name against a Cluster client configured with seed nodes, including how each one reacts to READONLY, MOVED, and ASK replies.

Consistency limits: async replication, partitions, and WAIT

Neither Sentinel nor Cluster gives you strong consistency, and the reason is the same in both: replication is asynchronous. The primary acknowledges a write to the client before any replica has received it. If the primary dies in that gap and a replica is promoted, the write is gone, even though the client was told it succeeded.

Partitions widen the gap. A primary cut off on the minority side has no idea it is about to be replaced, so it keeps accepting writes until it gives up. When the partition heals, it rejoins as a replica of the new primary and discards everything it accepted in the meantime.

Two settings shorten that window:

  • min-replicas-to-write 1
  • min-replicas-max-lag 10

With these, the primary refuses writes unless at least one replica has acknowledged it within the last 10 seconds. An isolated primary therefore stops accepting writes after roughly that interval instead of continuing indefinitely. The window is smaller but still open: writes within the lag interval can still be lost.

WAIT gives you per-write control. SET order:42 paid followed by WAIT 1 500 blocks until one replica confirms it received the write, or until 500 ms pass. It returns the number of replicas that acknowledged. That is all it does:

  • It does not make Redis strongly consistent.
  • A failover can still promote a replica that never received the write.
  • On timeout, nothing is rolled back. The write stays on the primary, and your code has to decide what a low return count means.

Redis Sentinel vs Redis Cluster: decision framework and alternatives

Start with capacity, not features. If the dataset and write throughput fit on one node with headroom, use Sentinel. If manual failover is acceptable, a primary with replicas and no Sentinel is simpler still. Choose Cluster only when memory or write throughput forces sharding and the application can live within slot constraints. Then check that your team can actually operate the topology you picked.

Illustrative estimate. Assume a 64 GB node. Reserve 30% for fragmentation, replication and client output buffers, and copy-on-write during snapshots, which leaves about 45 GB. Keep 30% of that free for growth, giving roughly 32 GB of usable dataset. A 20 GB dataset fits with room to spare. An 80 GB dataset does not fit, and a larger node only delays sharding.

Avoid Sentinel when writes or memory must scale. Replicas add read capacity only, and because replication is asynchronous they can serve stale data.

Avoid Cluster when the workload depends on cross-key transactions, uses multiple logical databases, or belongs to a team that cannot run and monitor more nodes.

Alternatives: a standalone instance with persistence and tested backups, a primary with replicas and manual promotion, or client-side or proxy-based sharding. The sharding options give up automatic resharding and push key routing into the client or proxy.

Sentinel Cluster
Purpose HA for one dataset HA plus horizontal scale
Sharding None 16,384 hash slots
Failover Sentinel quorum promotes a replica Primaries vote to promote a replica
Minimum topology 1 primary, replicas, 3 Sentinels 3 primaries, ideally with replicas
Multi-key ops Unrestricted Same slot only (hash tags)
Databases Multiple Database 0 only
Client Sentinel-aware discovery Handles MOVED/ASK redirects
Ops complexity Moderate High

Impact on cost, engineering time, and how to validate your choice

Cluster costs more to run and to build for. A typical production layout needs at least three primaries, each with a replica, plus ongoing work to rebalance slots as nodes are added and to reshard live data. The development cost often matters more. Multi-key operations need hash tags, anything that relies on SELECT must move to database 0, and every new key pattern needs a review so it does not end up spread across slots. Sentinel costs less. The main work is placing at least three Sentinels in separate failure domains so a quorum survives losing one zone, and making sure every client discovers the primary through Sentinel rather than a hardcoded address.

Cluster has an extra failure mode. If a shard loses its primary and all of its replicas, its slots have no owner. With cluster-require-full-coverage yes, the whole cluster stops serving queries. With no, the remaining shards keep serving, but requests for the uncovered slots fail.

Where engineering time goes

  • Choose the simpler mode when you can. If one node holds your data set and handles your write load, Sentinel avoids all of the Cluster work described above.
  • Design hash tags before launch. Changing key names later means migrating data while the old and new key formats coexist.
  • Standardize client timeouts and retries in a shared configuration, so each service does not end up with its own failover behavior.
  • Run failure drills. In a test environment, trigger SENTINEL FAILOVER <name>, run CLUSTER FAILOVER on a replica, use DEBUG SLEEP to simulate a hung primary (some deployments disable DEBUG), or stop a node outright. For each drill, measure three things: how long until writes resume, which errors clients raise, and which acknowledged writes are lost.

Set down-after-milliseconds or cluster-node-timeout based on what the drills show, not on defaults. A shorter timeout detects failure sooner but causes more false failovers.

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 sentinel vs redis cluster, 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.
  • Reviewers see the exact diff and the list of affected services up front, so approval is a decision rather than an investigation.

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.
  • Per-service approve/reject means a bad shared value can be refused where it would break something, before it is live.
  • 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.