JusDB
Free DB Audit
NoSQL Databases

Resharding Valkey 9 with Atomic Slot Migration: A Production Runbook

Reshard a Valkey 9 cluster with atomic slot migration: how it differs from MIGRATE, the checks to run first, how to monitor and cancel a move, and a batch runbook with rollback.

JusDB Team
Published October 30, 2026
Updated November 10, 2026
18 min read

Resharding a Valkey Cluster used to mean moving keys a batch at a time with MIGRATE. While a slot was half moved, clients chased -ASK redirects, multi-key commands failed with -TRYAGAIN, and one large sorted set could stall the move. Valkey 9.0 added atomic slot migration: the source primary streams whole slots to the target and hands ownership over in one step, driven by CLUSTER MIGRATESLOTS, CLUSTER GETSLOTMIGRATIONS and CLUSTER CANCELSLOTMIGRATIONS. It still has costs. The source forks, buffers writes to the moving slots, and pauses writes briefly at the end, and before 9.0.7 and 9.1.3 it could apply writes to the wrong database when cluster-databases is above 1. This runbook covers the checks, monitoring, cancelling and rollback.

TL;DR
  • Atomic slot migration (9.0 and later) copies slots with a forked snapshot plus streamed writes, then moves ownership once. Clients see one MOVED, not -ASK redirects.
  • Run 9.0.7 or 9.1.3 or later first: they fix wrong-database writes with cluster-databases above 1 (#4952) and unreplicated evictions on the target (#4920).
  • Run all three on the source primary. CANCELSLOTMIGRATIONS cancels every export from that node.
  • Budget source copy-on-write, the replica output buffer, and memory on the target primary and its replicas.
  • In 9.1, valkey-cli --cluster reshard and rebalance use it only with --cluster-use-atomic-slot-migration.

Scope and review date: November 2026. This post covers self-managed Valkey Cluster 9.0 and 9.1 (latest patches 9.0.7 and 9.1.3). Commands, defaults and messages were checked against the valkey.io documentation, the release notes and the 9.1.3 source on 10 November 2026. They were not run in a lab, so rehearse every step on staging.


How is atomic slot migration different from MIGRATE-based resharding?

Legacy resharding is driven from outside the cluster, one slot at a time. The Valkey deep dive on atomic slot migration lists the steps that valkey-cli --cluster reshard automates:

bash
# Legacy slot migration, repeated for every slot
CLUSTER SETSLOT <slot> IMPORTING <source-id>    # on the target
CLUSTER SETSLOT <slot> MIGRATING <target-id>    # on the source
CLUSTER GETKEYSINSLOT <slot> 10                 # on the source, until empty
MIGRATE <target-ip> 6379 "" 0 60000 KEYS k1 k2  # on the source, per batch
CLUSTER SETSLOT <slot> NODE <target-id>         # on the target, then the source

Interpretation: every batch costs network round trips, and valkey-cli defaults to 10 keys per batch. While the slot is split, the source answers -ASK for keys already moved, and a multi-key command spanning both nodes gets -TRYAGAIN. A large collection moves as one serialized payload.

Atomic slot migration moves the work into the server. Per the atomic slot migration topic page, the source authenticates to the target much as a replica would, forks a child that writes the slots as a stream of commands (collections travel as chunks of elements), then streams the writes made meanwhile. When little is left in flight, it pauses writes, the target catches up and claims the slots, and the source deletes its copy.

Atomic slot migration: snapshot, stream, handoff

Clients keep using the source until step 3; the only redirect is a MOVED after the handoff

Atomic slot migration in three phases Clients send reads and writes for the migrating slots to the source primary as normal, with no ASK redirects. Step 1: the source primary forks and sends a snapshot of the slots to the target primary, which keeps them hidden. Step 2: the source streams the writes made since the snapshot began. Step 3: the source briefly pauses writes, the target takes ownership and broadcasts it, and clients follow a MOVED redirect to the target. The client path to the source is retired for those slots. Clients cluster-aware Source primary owns the slots Target primary imports, hidden 1 2 1: forked snapshot 2: streamed writes traffic as normal, no ASK redirects 3 3: short write pause, ownership moves, clients follow MOVED

What is atomic: the ownership change. Data moves in the background, and the source owns and serves the slots until the target has caught up.

About the speed numbers

In Valkey's own 9.0.0 test (40 GB of 16 KB strings, valkey-cli --cluster rebalance), going from 3 to 4 shards took 1m42s legacy against 10.7 s atomic with no load, and 2m27s against 31 s under heavy load. Measure your own cluster.

What do clients see during an atomic slot migration?

Until the handoff, nothing changes. The source still owns the slots and serves every command for them, so there are no -ASK redirects and no -TRYAGAIN errors. On the target, importing slots are hidden from KEYS, SCAN, DBSIZE and INFO on the primary and its replicas.

At the handoff, the source pauses client writes with the same mechanism as a manual failover; reads continue. Once the target broadcasts ownership, the source deletes the keys and releases the paused clients, which follow MOVED. Valkey's benchmark shows a sharper but shorter throughput dip than legacy migration.

bash
valkey-cli -h 10.0.1.11 CONFIG GET cluster-manual-failover-timeout
valkey-cli -h 10.0.1.11 CONFIG GET slot-migration-max-failover-repl-bytes

Interpretation: the 9.1.3 valkey.conf says cluster-manual-failover-timeout (default 5000 ms) also bounds migration finalization; in the 9.1.3 source, writes stay paused for at most twice that, 10 seconds at the default. slot-migration-max-failover-repl-bytes (default 0) starts the pause only when the source's buffer is empty. Raising it lets a write-heavy slot reach the pause sooner, at the cost of a longer pause.

Legacy MIGRATE resharding and atomic slot migration side by side

Switch methods to see what changes for clients, large keys and failures

Legacy MIGRATE resharding

AreaLegacy
Who drives itAn external tool, one slot and key batch at a time
Client redirects during the move-ASK for keys already moved
Multi-key commands-TRYAGAIN while the slot is split
Large collectionsOne serialized payload; can stall or cause OOM
Cancel or failureReplay the steps in reverse by hand
Progress visibilityThe tool's own output

Atomic slot migration (9.0+)

AreaAtomic slot migration
Who drives itThe source primary, after one CLUSTER MIGRATESLOTS
Client redirects during the moveNone; one MOVED after the handoff
Multi-key commandsServed by the source; the slot is never split
Large collectionsSent as chunks of elements replayed as commands
Cancel or failureCLUSTER CANCELSLOTMIGRATIONS; the target discards its staged copy
Progress visibilityCLUSTER GETSLOTMIGRATIONS with state, cow_size and, from 9.1, remaining_repl_size

Both still exist: 9.0 kept legacy CLUSTER SETSLOT migration, and the cluster specification calls atomic slot migration the recommended method.

What must be true before you run CLUSTER MIGRATESLOTS?

The CLUSTER MIGRATESLOTS reference lists it since 9.0.0. The 9.1.3 source rejects it before anything starts in these cases:

  • Not the source primary. A replica answers "Slot migration can only be used on primary nodes.", and a node that does not own the range answers "Slots are not served by this node."
  • A legacy migration is open. Any slot in MIGRATING or IMPORTING state returns "Slots are being manually migrated" or "Slots are being manually imported".
  • A module has not opted in. Since 9.0.0-rc2, modules must declare support, or the command fails with "The module ... does not support atomic slot migrations."

Every node involved must also run 9.0 or later, since the target must speak the internal CLUSTER SYNCSLOTS protocol, and 9.0.7 or 9.1.3 is the floor for the fixes below.

bash
valkey-cli --cluster call 10.0.1.11:6379 INFO server | grep valkey_version
valkey-cli -h 10.0.1.11 CLUSTER NODES | grep -E '\[[0-9]+-(<|>)-'
valkey-cli --cluster call 10.0.1.11:6379 MODULE LIST

Interpretation: the second command must print nothing. A [slot->-id] or [slot-<-id] entry is an unfinished legacy move; close it with valkey-cli --cluster fix first.

The migration user needs write access

The source authenticates to the target as the primaryuser, and the target applies the data as ordinary commands under that user's ACL. A replica-only ACL works on empty slots and fails once a slot holds data. The topic page and the ACL requirements post give this rule string:

bash
# On every node; rule order matters
ACL SETUSER replicator on >change-me \
  +@write ~* -@dangerous +ping +select +psync +replconf \
  +cluster|syncslots -flushall -flushdb -restore -restore-asking

# Prove it on the target before the first move
valkey-cli -h 10.0.1.21 ACL DRYRUN replicator SET probe 1

Interpretation: OK means the user may write. If not, the target logs -NOPERM User replicator has no permissions to run the 'set' command, while the source reports only "Connection lost to target. Check CLUSTER GETSLOTMIGRATIONS on the target node for more information." The operator's user also needs the alldbs rule for MIGRATESLOTS and CANCELSLOTMIGRATIONS.

How much headroom does an atomic slot migration need?

When one of these runs short, the migration usually fails and cleans up, but a fork or memory squeeze can slow live traffic first.

  • Source fork. The snapshot child costs copy-on-write, reported as cow_size, and the job waits in waiting-to-snapshot while a BGSAVE or AOF rewrite child runs. Our fork latency post covers the fork cost itself.
  • Source output buffer. Writes made during the snapshot queue under client-output-buffer-limit replica (9.1.3 default 256mb 64mb 60). The topic page says to size both limits for them; an overrun fails the job.
  • Target memory. The target primary and each of its replicas must hold the incoming slots. An import that runs out fails with "Ran out of memory (OOM) during slot import".
bash
valkey-cli -h 10.0.1.11 CLUSTER SLOT-STATS SLOTSRANGE 0 511
valkey-cli -h 10.0.1.11 CLUSTER SLOT-STATS ORDERBY key-count LIMIT 10 DESC
valkey-cli --cluster call 10.0.1.11:6379 INFO memory | grep -E 'used_memory:|maxmemory:'

Interpretation: CLUSTER SLOT-STATS (since 8.0) returns key-count per slot, plus CPU and network bytes when cluster-slot-stats-enabled is yes, which finds the write-heavy slots that grow the buffer. The range's share of keys times the source's used_memory gives a rough size; keep the target well under maxmemory after adding it.

Full targets before 9.0.7 and 9.1.3

A target at maxmemory evicts its own keys to make room, and before these patches those evictions were not replicated. PR #4920 shows the target's replicas growing past maxmemory within a minute in a scale-in and being OOM-killed.

How do you start and monitor an atomic slot migration?

Get the target primary's node ID and send MIGRATESLOTS to the source. Repeat SLOTSRANGE and NODE blocks to start several jobs at once; the command returns OK only if all of them start.

bash
TARGET=$(valkey-cli -h 10.0.1.21 CLUSTER MYID)
valkey-cli -h 10.0.1.11 CLUSTER MIGRATESLOTS SLOTSRANGE 0 511 NODE "$TARGET"

Interpretation: OK means the job started, not that it finished. The work is asynchronous, so poll next.

The CLUSTER GETSLOTMIGRATIONS reference lists one entry per job on the source primary, the target primary and the target's replicas. Terminal states are success, failed and cancelled. In the 9.1.3 source an export passes through snapshotting, replicating, waiting-to-pause, failover-paused and failover-granted, after some connection states.

CLUSTER GETSLOTMIGRATIONS on the source during a move

Illustrative output: field names from the 9.1 reference, values invented

valkey-cli: source primary 10.0.1.11
$ valkey-cli -3 -h 10.0.1.11 -p 637910.0.1.11:6379> CLUSTER GETSLOTMIGRATIONS1) 1# "name" => "c31f0a9e7d2b4c86a1e5f09b3d7c2a6e8f41b0d2" 2# "operation" => "EXPORT" 3# "slot_ranges" => "0-511" 4# "target_node" => "4b4f12fdfb58d5e30fef7b9ad3f1651dacbbaba9" 5# "source_node" => "93941e777e17fcbc92d4398cc957ffea888f472b" 6# "create_time" => (integer) 1794297600 7# "last_update_time" => (integer) 1794297614 8# "last_ack_time" => (integer) 1794297641 9# "state" => "replicating" 10# "message" => "" 11# "cow_size" => (integer) 18874368 12# "remaining_repl_size" => (integer) 4194304

Read three fields: state gives the phase, remaining_repl_size (9.1 and later) shows whether streaming keeps up, and last_ack_time should stay within seconds of now.

bash
# Poll the source every 5 seconds until a terminal state
while true; do
  out=$(valkey-cli -h 10.0.1.11 CLUSTER GETSLOTMIGRATIONS)
  state=$(printf '%s\n' "$out" | grep -m1 -A1 '^state$' | tail -1)
  echo "$(date +%T) $state"
  case "$state" in success|failed|cancelled) break ;; esac
  sleep 5
done
# The source log records each phase
grep -E 'Slot migration initiated|Pausing writes|now owned by target|Write loss risk' \
  /var/log/valkey/valkey.log

Interpretation: piped output puts one field per line, and the newest job comes first, so the first state match is the current job. A remaining_repl_size that keeps growing means writes outpace the link and the job will not reach the pause. Jobs live only in memory, so a restart clears them and cluster-slot-migration-log-max-len (default 1000) caps the finished ones kept; the log is your durable record.

How do you cancel a slot migration, and what happens when one fails?

The CLUSTER CANCELSLOTMIGRATIONS reference says it cancels all in-progress migrations started on that node. There is no per-job cancel, and a target cannot cancel an import.

bash
valkey-cli -h 10.0.1.11 CLUSTER CANCELSLOTMIGRATIONS
# Every migration in the cluster
valkey-cli --cluster call 10.0.1.11:6379 CLUSTER CANCELSLOTMIGRATIONS --cluster-only-primaries

Interpretation: OK moves each export to cancelled; "No migrations ongoing" means that node was exporting nothing. The target's staged slots were never announced, so the source keeps ownership and all its data.

The 9.1.3 state machine lists the causes of an automatic failure: a lost connection, no acknowledgement for longer than repl-timeout (default 60 s), OOM on the target, a source buffer overrun, a failover or ownership change on either shard, FLUSHDB or FLUSHALL on either node, or a failed snapshot child. The message field names the cause, and the target's entry often has more. Fix it and start again; the retry begins from scratch.

Alert on "Write loss risk"

If the target is granted the handoff but does not broadcast ownership before the pause expires, for example after a crash or a partition, the source unpauses and logs "Write loss risk! ... Any writes we have recorded since unpausing may be lost!". The job message reads "Unpaused before migration completed (migration may have succeeded with lost writes)". Treat it as an incident: confirm which node owns the slots and reconcile recent writes.

Can valkey-cli reshard and rebalance with atomic slot migration?

From 9.1.0, if you ask. PR #2755 added --cluster-use-atomic-slot-migration to --cluster reshard and --cluster rebalance, as the 9.1 release post notes. Without it, both still use MIGRATE. The 9.0.7 valkey-cli has no atomic slot migration code, so on 9.0 send the commands yourself.

bash
valkey-cli --cluster rebalance 10.0.1.11:6379 --cluster-simulate
valkey-cli --cluster rebalance 10.0.1.11:6379 --cluster-use-atomic-slot-migration

valkey-cli --cluster reshard 10.0.1.11:6379 \
  --cluster-from "$SOURCE" --cluster-to "$TARGET" --cluster-slots 512 \
  --cluster-yes --cluster-use-atomic-slot-migration

Interpretation: with the flag, valkey-cli sends one MIGRATESLOTS per source and target pair, polls every 100 ms, and prints the job's error on failure or cancellation. In 9.1.3 the flag is missing from the --cluster help option list, so the help text will not confirm it.

A documentation gap

The cluster tutorial shows only legacy resharding and says "there is no way for valkey-cli to automatically rebalance the cluster checking the distribution of keys". --cluster rebalance exists, but it balances slot counts by node weight (default threshold 2%), not keys or memory, so hot or oversized slots can look balanced to it.

How does atomic slot migration interact with replicas and older versions?

The target primary forwards the migration stream to its replicas, which import alongside it. If the target shard fails over mid-migration, the import fails and you rerun the job against the new primary.

Older replicas are the trap. PR #2735 lets a 9.1 or later primary full-sync 7.2 and 8.x replicas in RDB 11, but "if any new features that the replica can't handle (such as hash-field expiration and atomic slot migration) are in use, the full sync is aborted". A node with an active import logs "Can't store slot migrations in RDB version 11". The RDB version and rollback guide covers that mixed-version window.

bash
grep "Can't store slot migrations in RDB version" /var/log/valkey/valkey.log

Interpretation: any hit means an old replica lost its full sync during a move. Finish the version upgrade on every node before you reshard.

What changes when cluster-databases is greater than 1?

Valkey 9.0 allows numbered databases in cluster mode through cluster-databases (default 1, not changeable at runtime). The snapshot selects each database with keys in the moving slots, but PR #4952 explains that the parent never saw those SELECT commands, so a write streamed afterwards could run in the wrong database on the target. The 9.0.7 and 9.1.3 release notes list the fix.

bash
valkey-cli -h 10.0.1.11 CONFIG GET cluster-databases
valkey-cli --cluster call 10.0.1.11:6379 INFO keyspace

Interpretation: at 1, the bug does not apply. Above 1, if you moved busy slots on 9.0.6 or 9.1.2 or older, look for keys in unexpected databases on the new owner; the fix does not repair data already moved. The migration user also needs +select. Legacy valkey-cli --cluster reshard loops over every database, although the cluster specification still says MIGRATE needs no database other than 0.

What is a safe resharding runbook with rollback?

Move slots in batches you can afford to cancel, and check the cluster between batches.

Resharding runbook with atomic slot migration

Eight steps; step 6 holds the only write pause, on the source primary

  1. Confirm versions on every node

    9.0.7 or 9.1.3 or later everywhere, with no rolling upgrade in progress.

    valkey-cli --cluster call 10.0.1.11:6379 \ INFO server | grep valkey_version
  2. Check the cluster is clean

    All slots covered, nodes agree, and no slot left open by a legacy reshard.

    valkey-cli --cluster check 10.0.1.11:6379
  3. Size the batch and check headroom

    Pick a range the target holds with room to spare, outside BGSAVE windows.

    valkey-cli -h 10.0.1.11 CLUSTER SLOT-STATS \ SLOTSRANGE 0 511
  4. Prove the migration user can write

    Run on the target. Anything but OK fails the move once a slot has data.

    valkey-cli -h 10.0.1.21 ACL DRYRUN replicator \ SET probe 1
  5. Start one batch from the source primary

    Record the time and range in the ticket.

    valkey-cli -h 10.0.1.11 CLUSTER MIGRATESLOTS \ SLOTSRANGE 0 511 NODE "$TARGET"
  6. Watch it to a terminal state

    Brief write pause

    At finalization the source pauses writes, for at most twice cluster-manual-failover-timeout.

    valkey-cli -h 10.0.1.11 CLUSTER GETSLOTMIGRATIONS
  7. Cancel if a limit is crossed

    Stop if remaining_repl_size keeps growing, memory nears your limit, or latency alerts fire.

    valkey-cli -h 10.0.1.11 CLUSTER CANCELSLOTMIGRATIONS
  8. Verify, then run the next batch

    Confirm the new owner and an empty log search before the next range.

    valkey-cli --cluster check 10.0.1.21:6379 grep 'Write loss risk' /var/log/valkey/valkey.log

Rollback point: before success, cancel and the source keeps the slots and data. After success, rolling back means migrating the range back from the new owner.

bash
# Roll back a finished batch: the new owner is now the source
ORIGINAL=$(valkey-cli -h 10.0.1.11 CLUSTER MYID)
valkey-cli -h 10.0.1.21 CLUSTER MIGRATESLOTS SLOTSRANGE 0 511 NODE "$ORIGINAL"

Interpretation: this is another migration with the same headroom checks, so the original node must still have room. Legacy valkey-cli --cluster reshard without the flag remains a fallback, with its -ASK and large-key costs.

Frequently asked questions

What is atomic slot migration in Valkey?

Atomic slot migration is the resharding method added in Valkey 9.0. The source primary sends a forked snapshot of the slots to the target, streams later writes, briefly pauses writes, and then transfers ownership in one step. You start it with CLUSTER MIGRATESLOTS, monitor it with CLUSTER GETSLOTMIGRATIONS, and cancel it with CLUSTER CANCELSLOTMIGRATIONS. Legacy MIGRATE-based migration still exists.

Does Valkey atomic slot migration block clients?

Only briefly. Clients keep using the moving slots on the source with no ASK redirects. At finalization the source pauses client writes until the target takes ownership, then clients follow a MOVED redirect. In the 9.1.3 source the pause lasts at most twice cluster-manual-failover-timeout, 10 seconds at the default 5000 ms, before the migration fails.

How do I cancel a Valkey slot migration?

Run CLUSTER CANCELSLOTMIGRATIONS on the source primary. It cancels every in-progress migration that node started; you cannot cancel one job by name, and the target cannot cancel its import. The target discards its staged copy, so the source keeps ownership and all its data.

Which Valkey version should I run before using atomic slot migration?

Run 9.0.7 or 9.1.3, or a later patch, on every node. Those October 2026 releases fix writes applied to the wrong database during a migration when cluster-databases is above 1, and evictions on the import target not being replicated. Both also carry a security fix.

Key takeaways

  • Atomic slot migration replaces per-key MIGRATE with a snapshot, streamed writes and one ownership change: no -ASK, one MOVED.
  • Patch to 9.0.7 or 9.1.3, close any open legacy migration, and give the primaryuser write access first.
  • Size each batch against source copy-on-write, the replica output buffer and target memory, and watch remaining_repl_size.
  • Cancel on the source, and alert on "Write loss risk" in the source log.
  • In 9.1, add --cluster-use-atomic-slot-migration to valkey-cli, and finish version upgrades before you reshard.

Plan a Valkey Cluster reshard with JusDB

JusDB's Valkey Cluster team can review your shard sizes, versions and ACLs, plan slot batches that fit your headroom, and write a resharding runbook with cancel and rollback steps for your cluster.

Bring your topology, node versions, cluster-databases setting, memory figures, and your maintenance windows. Your team approves every production change.

Share this article

Database engineering notes

Articles like this one, in your inbox. No spam, unsubscribe anytime.

JusDB Team

Official JusDB content team

Keep reading

Moving a Multi-Database Valkey Instance into a 9.x Cluster

Move a Valkey instance that uses SELECT 1 to 15 into a 9.x cluster: keep or remap databases, copy each one, verify counts per database, and keep a way back.

Redis18 minNov 10, 2026
Read

Valkey Keyspace Notifications in Production: Expired Events, hexpired and Cluster Mode

Run Valkey keyspace notifications in production: why expired and hexpired events lag the TTL, how to subscribe on every cluster node, and when a stream is safer.

Redis20 minNov 10, 2026
Read

Valkey GLIDE in Production: AZ-Affinity Reads and Migrating from Legacy Clients

Configure Valkey GLIDE AZ-affinity reads on servers and clients, learn what GLIDE 2.5.3 does not support, and migrate from redis-py, Jedis, ioredis, or go-redis one service at a time.

Redis20 minNov 10, 2026
Read