Redis High Availability
One master with multiple replicas—one master node and many replica nodes. It supports master-replica sync and replica-of-replica sync. The latter mainly relieves the master’s replication burden; a replica can itself have replicas.
Incremental sync: Redis replicates a stream of commands.
Snapshot sync: the master runs a bgsave to produce an RDB file; the replica loads that RDB file, after first clearing the data currently in memory.
Adding a replica: when a replica first joins the cluster, it does a snapshot sync, then continues with incremental sync.
Diskless replication: the master sends the snapshot content directly to the replica over a socket. Producing the snapshot is a traversal process—the master traverses memory while serializing and sending content to the replica, which stores what it receives into a disk file and then loads it all at once.
The core purpose of Redis HA is to guarantee service availability and keep cached data available (avoiding a full cache flush). Rebuilding the cache takes time and impacts the business. When the master fails and a replica is promoted, losing a small amount of cached data is acceptable—it has little business impact. Why is synchronous replication unnecessary? Because Redis’s whole value is its speed and high performance; if synchronous replication caused a large performance loss, Redis would lose its biggest advantage and its core business value.
Redis Replication
Redis replication is split into full resync and partial resync.
Full resync process: the master starts a background save process to generate the RDB file, while also buffering all newly received write commands. When the background save completes, the master transfers the database file to the replica, which saves it to disk and then loads it into memory. The master then sends all buffered commands to the replica. This is done as a stream of commands, in the same format as the Redis protocol itself.

Master log
9412:M 20 Oct 2025 17:06:26.195 - Accepted 127.0.0.1:42354
9412:M 20 Oct 2025 17:06:26.199 * Replica 127.0.0.1:6380 asks for synchronization
9412:M 20 Oct 2025 17:06:26.199 * Full resync requested by replica 127.0.0.1:6380 # full resync requested
# create the replication backlog
9412:M 20 Oct 2025 17:06:26.199 * Replication backlog created, my new replication IDs are '756cf5352890fd5a090c7809e968c5198905aed9' and '0000000000000000000000000000000000000000'
# create the RDB file for full resync
9412:M 20 Oct 2025 17:06:26.199 * Starting BGSAVE for SYNC with target: disk
9412:M 20 Oct 2025 17:06:26.200 * Background saving started by pid 9498
9498:C 20 Oct 2025 17:06:26.202 - RDB: 0 MB of memory used by copy-on-write
9498:C 20 Oct 2025 17:06:26.207 * DB saved on disk
9498:C 20 Oct 2025 17:06:26.208 * RDB: 0 MB of memory used by copy-on-write
9412:M 20 Oct 2025 17:06:26.306 * Background saving terminated with success
9412:M 20 Oct 2025 17:06:26.307 * Synchronization with replica 127.0.0.1:6380 succeeded
Replica log:
# establish connection to the master
9493:S 20 Oct 2025 17:06:26.194 * Connecting to MASTER 127.0.0.1:6379
9493:S 20 Oct 2025 17:06:26.195 * MASTER <-> REPLICA sync started
9493:S 20 Oct 2025 17:06:26.195 * Non blocking connect for SYNC fired the event.
9493:S 20 Oct 2025 17:06:26.196 * Master replied to PING, replication can continue...
9493:S 20 Oct 2025 17:06:26.198 * Partial resynchronization not possible (no cached master)
# request a full resync
9493:S 20 Oct 2025 17:06:26.201 * Full resync from master: 756cf5352890fd5a090c7809e968c5198905aed9:0
9493:S 20 Oct 2025 17:06:26.307 * MASTER <-> REPLICA sync: receiving 7842 bytes from master to disk
9493:S 20 Oct 2025 17:06:26.307 * MASTER <-> REPLICA sync: Flushing old data
9493:S 20 Oct 2025 17:06:26.308 * MASTER <-> REPLICA sync: Loading DB in memory
9493:S 20 Oct 2025 17:06:26.313 * Loading RDB produced by version 6.2.18
9493:S 20 Oct 2025 17:06:26.313 * RDB age 0 seconds
9493:S 20 Oct 2025 17:06:26.313 * RDB memory usage when created 2.31 Mb
9493:S 20 Oct 2025 17:06:26.313 # Done loading RDB, keys loaded: 19, keys expired: 0.
9493:S 20 Oct 2025 17:06:26.313 * MASTER <-> REPLICA sync: Finished with success
Master-replica replication
Three ways to configure master-replica replication:
- Method 1: the
replicaof <masterip> <masterport>command, configured in the replica’s redis.conf. - Method 2: use the redis client (redis-cli) on the replica with
replicaof <masterip> <masterport>. - Method 3: specify the master at replica startup with
redis-server redis.conf replicaof <masterip> <masterport>.
The format of command-line arguments is identical to that used in
redis.conf, except the keyword prefix is--.
Example:
127.0.0.1:6381> replicaof 127.0.0.1 6380 # configure as a replica
OK
127.0.0.1:6381> info replication # inspect replica info
# Replication
role:slave
master_host:127.0.0.1
master_port:6380
master_link_status:up
# ...
Stopping replication: a replica can run replicaof no one to stop replicating; the node becomes a master and does not discard the data it has already replicated.
When using the replicaof command, note that if the current node is already a replica, running replicaof <masterip> <masterport> stops replication from the old master, begins replication from the specified new master, and discards the old dataset.
Master-replica replication mechanism:
- When the master and replica are well connected, the master syncs the replica by sending it a stream of commands.
- When the connection drops, the replica reconnects and attempts a partial resync, trying to fetch only the part of the command stream missed during the disconnect.
- When partial resync is not possible, the replica requests a full resync.
Redis uses asynchronous replication by default; replicas periodically acknowledge to the master, asynchronously, how much data they have received. Clients can use the WAIT command to request synchronous replication for specific data.
The WAIT command
WAIT numreplicas timeout blocks the current client until all previous write commands have been transmitted successfully and received by at least numreplicas replicas. If the timeout value (ms) is reached, the command returns even if the required number of replicas is not met. A timeout of 0 means block forever.
WAIT does not make Redis a strongly consistent store. It mainly improves data safety in Sentinel or Redis Cluster failover scenarios. After using WAIT, you can be sure that, within the current connection, all writes before the WAIT command have been received by the number of replicas the command returned.
Configuration details
replicaof <masterip> <masterport> makes a Redis instance a replica of another.
-
- Redis replication is asynchronous, but you can configure the master to stop accepting writes when disconnected from a specified number of replicas.
-
- If a replication connection is interrupted briefly, the replica can perform a partial resync. Size the replication backlog according to your needs.
-
- Replication proceeds automatically; after a network partition the replica auto-reconnects and resyncs with the master.
masterauth <password> required when the master is password-protected; authentication must happen before replication starts.
masteruser <username> needed if the default user lacks the permissions replication (e.g. PSYNC) requires. Defaults to the default user if unspecified. When the replica’s connection to the master is interrupted or it is syncing, two behaviors apply:
- When
replica-serve-stale-dataisyes(the default), it keeps responding to client requests, possibly returning stale data; on a first sync the dataset may be empty. - When
replica-serve-stale-dataisno, it returns a “SYNC with master in progress” error for all requests except a specified set of commands (INFO, REPLICAOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG, SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, COMMAND, POST, HOST and LATENCY).
repl-diskless-sync no full-resync strategy, disk or socket.
- Disk mode: the master spawns a child to generate the RDB file, then sends the file to the replica.
- Diskless mode: the master sends the RDB straight to the replica over a socket, without touching disk. Diskless replication can be configured with a wait time to support parallel transfer to multiple replicas.
repl-diskless-sync-delay 5 the diskless-replication delay. When several replicas request a full resync almost simultaneously, this allows batched sync—fewer repeated forks and RDB generations, sharing one RDB snapshot transfer for efficiency. Workflow: the first replica requests a full resync; the master does not start immediately but waits repl-diskless-sync-delay seconds, during which other replicas can connect and request sync. After the delay, the master generates one RDB snapshot and sends it over the network to all replicas that queued during the delay. Setting it to 0 starts diskless sync immediately. A higher value (e.g. 5–10 seconds) is suggested for failover-recovery scenarios; lower for others (dynamic scaling, etc.).
repl-diskless-load disabled how the replica loads the RDB (diskless load is experimental—use with care, as it may lose data on failover).
- disabled: store to disk first, then load
- on-empty-db: diskless load only when safe
- swapdb: keep an in-memory copy of the current data. Diskless load: the replica can load the RDB directly from the socket, without storing it to disk first.
repl-ping-replica-period 10 the interval at which the replica pings the master; default 10 seconds, to ensure the connection is alive.
repl-timeout 60 defines the timeout threshold for three scenarios:
- Replica view, full-resync SYNC bulk-transfer timeout: during a full resync, the master generates the RDB and transfers it over the network; if transfer exceeds
repl-timeout, the replica deems the sync failed, abandons it, and retries a full resync. - Replica view, master data/heartbeat timeout: during normal replication, if the replica receives no master data or heartbeat for longer than
repl-timeout, it considers the master unavailable, drops the connection, and tries to re-establish it. - Master view, replica heartbeat-response timeout: the master uses REPLCONF ACK to periodically check replica liveness; if a replica fails to respond for longer than
repl-timeout, the master deems it unavailable and drops the connection.repl-timeoutmust be greater thanrepl-ping-replica-period. In practice, don’t set it too small, since the network is the least stable factor.
repl-disable-tcp-nodelay no disables TCP_NODELAY on the replica socket (default no, low latency preferred; yes saves bandwidth but adds ~40ms latency).
Replication backlog config (affects partial-resync capability): when a replica disconnects, the master keeps the replicated data in the backlog buffer, so on reconnect the replica need not re-copy the full dataset—only the missed part.
repl-backlog-size 1mb # buffer size (allocated when at least one replica is connected)
// create the replication backlog; no need to persist it, it lives only in memory and affects partial-resync capability; even if lost, full resync can recover
void createReplicationBacklog(void) {
serverAssert(server.repl_backlog == NULL); // ensure no replication backlog exists currently
server.repl_backlog = zmalloc(server.repl_backlog_size);
server.repl_backlog_histlen = 0;
server.repl_backlog_idx = 0;
/* We don't have any data inside our buffer, but virtually the first
* byte we have is the next byte that will be generated for the
* replication stream. */
server.repl_backlog_off = server.master_repl_offset+1;
}
repl-backlog-ttl 3600 # how long (seconds) to keep the backlog after all replicas disconnect; 0 means never release the backlog.
Replica priority (used by Sentinel for leader election; 0 means cannot be promoted; default 100)
replica-priority 100
replica-announced yes controls whether this node is discovered and monitored by Sentinel. When set to no, it does not appear in the output of sentinel replicas <master>, and Sentinel is unaware of its existence. Even set to no, it can still participate in failover and may be promoted. To fully forbid a replica from becoming master, you must also set its replica-priority to 0. Useful for hiding replicas used for backup, testing, or high cross-region latency, keeping them out of normal service discovery while still syncing data.
min-replicas-to-write 3 the minimum number of online replicas required. When fewer than N valid replicas are connected and their lag is all less than or equal to M seconds, the master may stop accepting writes. These N replicas must be “online”. The lag is computed from the last ping the replica received and must be within the specified value. This option does not guarantee writes are received by N replicas, but when too few replicas are available it bounds the data-loss risk window to the specified seconds. For example, require at least 3 replicas with lag within 10 seconds. Setting it to 0 disables this feature, letting the master write even with no replicas.
min-replicas-max-lag 10 the maximum allowed replication lag (seconds).
replica-announce-ip 5.5.5.5 | replica-announce-port 1234 key configs for correct master-replica recognition in complex networks (NAT, Docker, cloud). Under NAT/port-forwarding, a replica’s auto-detected local IP/port may be an internal address that is unreachable from the master or other external services. The manual declaration mechanism fixes this: replica-announce-ip declares the replica’s externally reachable IP to the master; replica-announce-port declares its externally reachable port. Both are configured on the replica.
Redis replication internals
Every Redis master instance has an ID: a large pseudo-random string marking the history of a given dataset. Each master also has an offset that increments for every byte of replication stream generated (sent to replicas to update their state with new changes). The replication offset increments even with no replica actually connected, so basically every pair of Replication ID, offset (run ID and offset) identifies the exact version of the master’s dataset.
void changeReplicationId(void) {
getRandomHexChars(server.replid,CONFIG_RUN_ID_SIZE);
server.replid[CONFIG_RUN_ID_SIZE] = '\0';
}
When a replica connects to a master, it uses the PSYNC command to send its old master’s run ID and the offset it has processed so far. The master can then send the needed incremental part. But if the master’s buffer lacks enough backlog, or the replica references a history (run ID) that is no longer known, a full resync occurs, and the replica fetches the full dataset from scratch.
The run ID basically marks a given history of the data. Each time an instance restarts from scratch as a master, or a replica is promoted to master, a new run ID is generated for that node. A replica connecting to a master inherits its run ID after the handshake. Thus two instances with the same ID are related by holding the same data, possibly at different times. It is the offset that acts as logical time, telling us who holds the newest dataset under a given history (run ID).
For example, if nodes A and B share the same run ID but one has offset 1000 and the other 1023, the first is missing some commands applied to the dataset. It also means A can reach exactly B’s state by applying a few commands.
A Redis instance has two run IDs because a replica can be promoted to master. After failover, the promoted replica must still remember its past run ID, since that ID is the old master’s. This way, when other replicas sync with the new master, they try a partial resync using the old master’s run ID, which works as expected: when a replica is promoted, it sets its secondary ID to the old master’s run ID and remembers the offset at the switch. It then picks a new random run ID because a new history begins. When handling newly connecting replicas, the master matches their ID and offset using both the current ID and the secondary ID. In short, this means that after failover, replicas connecting to the newly promoted master need not perform a full sync.
void shiftReplicationId(void) {
memcpy(server.replid2,server.replid,sizeof(server.replid));
/* We set the second replid offset to the master offset + 1, since
* the slave will ask for the first byte it has not yet received, so
* we need to add one to the offset: for example if, as a slave, we are
* sure we have the same history as the master for 50 bytes, after we
* are turned into a master, we can accept a PSYNC request with offset
* 51, since the slave asking has the same history up to the 50th
* byte, and is asking for the new bytes starting at offset 51. */
server.second_replid_offset = server.master_repl_offset+1;
changeReplicationId();
serverLog(LL_WARNING,"Setting secondary replication ID to %s, valid up to offset: %lld. New replication ID is %s", server.replid2, server.second_replid_offset, server.replid);
}
Why must a replica promoted to master change its run ID after failover? There is a possibility that the old master is still acting as master due to a network partition; keeping the same run ID would violate the fact that “any two random instances with the same ID and same offset have the same dataset.”
State of a one-master-one-replica setup:
127.0.0.1:6379> info replication
# Replication
role:master # master node
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=14,lag=0
master_failover_state:no-failover
master_replid:f0c92deb38de697d6647b3457773b811b6196573 # run ID
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14 # offset
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576 # replication backlog size
repl_backlog_first_byte_offset:1 # backlog start offset
repl_backlog_histlen:14 # data length in the backlog
127.0.0.1:6380> info replication
# Replication
role:slave # replica node
master_host:127.0.0.1
master_port:6379
master_link_status:up # master connection status, up means normal
master_last_io_seconds_ago:2
master_sync_in_progress:0 # whether syncing
slave_read_repl_offset:56 # replica read replication offset
slave_repl_offset:56 # replica replication offset
slave_priority:100 # failover priority
slave_read_only:1
replica_announced:1 # replica announced by Sentinel
connected_slaves:0
master_failover_state:no-failover
master_replid:f0c92deb38de697d6647b3457773b811b6196573 # replica's run ID equals the master's
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:56
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:56
Shut down the master and promote the replica to master:
127.0.0.1:6380> replicaof no one # promote the replica to master
OK
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:84a5cb4c551897e90f38cef233939ede49950ff7 # new master's run ID
master_replid2:f0c92deb38de697d6647b3457773b811b6196573 # old master's run ID
master_repl_offset:1568
second_repl_offset:1569
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1568
How Redis replication handles expired keys
Expired keys depend on the clock, and clocks across nodes can drift, so Redis replication handles expired keys by NOT relying on master and replica having synchronized clocks (e.g. NTP), because clock sync always has a precision limit and can never be perfectly synchronized.
- A replica does not expire keys itself; it waits for the master to expire them. When the master expires a key (or evicts via LRU), it synthesizes a
DELcommand and transmits it to all replicas. - Because expiration is master-driven, a replica’s memory may sometimes still hold logically expired keys, since the master failed to sync the
DELin time. To address this, the replica uses a logical clock and only reports a key as absent in read operations that do not violate dataset consistency. This way the replica avoids reporting keys that are logically expired but still present. - During Lua script execution, key expiration does not occur. Conceptually, while a Lua script runs, the master’s time is frozen, so a given key is present or absent for the whole script. This prevents keys from expiring mid-script and is required so the same script can be sent to replicas and produce the same effect on the dataset.
Once a replica is promoted to master, it begins expiring keys independently and no longer needs any help from its old master.
By default, a replica ignores maxmemory; key eviction is handled by the master, which sends a DEL command to replicas when it evicts.
References: Redis replication Redis replication (zh) Redis replication (cn)