Many standalone and Sentinel-managed Valkey instances keep separate data sets in SELECT 1 to SELECT 15. Until Valkey 9.0, a cluster had only database 0, so those instances could not move. Valkey 9.0 added numbered databases in cluster mode, but the tools lag behind. First attempts usually fail in one of three ways. Clients get ERR DB index is out of range because cluster-databases is still 1. A node loading the old RDB file exits with FATAL: Data file was created with a Valkey server configured to handle more than 1 databases. Or valkey-cli --cluster import prints *** Importing N keys from DB 0, leaves databases 1 to 15 behind, and deletes the keys it moved from the source. This guide covers what 9.x allows, keeping databases versus remapping to key prefixes, moving each database, verification, and rollback.
- Set
cluster-databasesabove the highest database id in use, on every node, before first start. It defaults to 1 and cannot change at runtime. valkey-cli --cluster importmoves database 0 only, and it deletes source keys unless you pass--cluster-copy. Script the other databases, or load the RDB file into one empty node.- Run 9.0.7 or 9.1.3 or later before you use atomic slot migration with more than one database. Earlier 9.x releases could apply writes to the wrong database on the target (#4952).
- Check every client: GLIDE 2.1 and later can select a database in cluster mode; valkey-py's
ValkeyClusterrejectsdb. - Verify per database by summing
INFO keyspaceacross primaries.valkey-cli --cluster checkcounts database 0 only.
Scope and review date: November 2026. This post covers moving a standalone or Sentinel-managed Valkey 7.2, 8.x or 9.x instance (or Redis OSS 7.2 and earlier) into a self-managed Valkey 9.0.7 or 9.1.3 cluster. Commands, defaults and error strings were checked against the Valkey documentation and the 9.1.3 source code on 10 November 2026. They were not run in a lab, so rehearse them on staging first.
What do numbered databases in Valkey 9 cluster mode allow and restrict?
The cluster specification says Valkey Cluster has supported multiple databases since 9.0, "with some additional restrictions", but does not list them. The list below comes from the original change, PR #1671, and the 9.1.3 source:
cluster-databasesdefaults to 1 and is immutable inconfig.c. The standalonedatabasessetting is ignored in cluster mode.- The slot depends only on the key name, so the same key in databases 0 and 5 shares a slot.
SELECT,MOVEandCOPY ... DBwork.SWAPDBreturnsSWAPDB is not allowed in cluster mode.MOVEandCOPYwith a database returnTRYAGAINwhile their slot is migrating.CLUSTER GETKEYSINSLOT,CLUSTER COUNTKEYSINSLOTandMIGRATEact on the currently selected database.FLUSHDB,SCANandDBSIZEact on the connected node only (Valkey blog: Numbered Databases in Valkey 9.0).- Databases share CPU and memory: a namespace, not a resource boundary.
# valkey.conf on every cluster node, before the first start
cluster-enabled yes
cluster-databases 16Interpretation: valid ids then run from 0 to 15, so size the value to the highest id in use, not the number of databases. CONFIG SET cluster-databases fails with can't set immutable config, and SELECT 3 on a node still at the default returns ERR DB index is out of range. If your topology choice is still open, read Valkey Cluster vs Sentinel first.
How do I find which databases the source actually uses?
Start with the keyspace section of INFO. It lists only databases that hold keys, one line per database.
valkey-cli -h 10.0.0.5 INFO keyspace
valkey-cli -h 10.0.0.5 CONFIG GET databasesInterpretation: each dbN:keys=...,expires=... line is one database to move. The highest N sets the minimum cluster-databases value. Record the keys and expires figures now; they are your baseline for verification.
Then find which clients use which database. The SELECT documentation notes that CLIENT LIST shows a db=N field for each connection.
valkey-cli -h 10.0.0.5 CLIENT LIST \
| awk '{for (i = 1; i <= NF; i++) if ($i ~ /^(name|db|user)=/) printf "%s ", $i; print ""}' \
| sort | uniq -cInterpretation: each row counts connections per client name, database and ACL user. A database with keys but no live connections may belong to a batch job, so check cron hosts too.
Should you keep numbered databases or remap them to key prefixes?
Keeping databases is the smaller application change: the database number stays in the connection settings. Remapping to prefixes (db3: in front of every key) works on any cluster version and any cluster client, but it touches every key name in the code.
Keep numbered databases or remap to key prefixes
Switch options to compare what changes in the server, the clients and the migration
Keep numbered databases
| Area | Keep numbered databases |
|---|---|
| Server version | Valkey 9.0 or later in cluster mode |
| Server config | cluster-databases above the highest id, set before first start |
| Application change | Database number in the client configuration |
| Client library | Must allow a non-zero database in cluster mode |
| Same key name in two databases | Stays two separate keys |
| Memory cost | Small overhead per database, not per key |
| Copy method | MIGRATE with destination-db N, or RDB load |
| Moving back to an 8.x cluster | Not possible without remapping first |
Remap to key prefixes
| Area | Remap to key prefixes |
|---|---|
| Server version | Any Valkey Cluster version |
| Server config | Default cluster-databases 1 |
| Application change | Every key name gets a prefix |
| Client library | Any cluster client |
| Same key name in two databases | Prefix keeps them apart in database 0 |
| Memory cost | Prefix bytes repeated on every key |
| Copy method | DUMP, then RESTORE under the new name |
| Moving back to an 8.x cluster | Possible: data uses database 0 only |
Deciding factor: client support. If one service's cluster client cannot select a database, either upgrade that client or remap that service's database to a prefix.
Client support varies. The Valkey GLIDE configuration reference documents databaseId, which in cluster mode "requires Valkey 9.0+" and persists across reconnections; the GLIDE 2.1 changelog lists it for Node, Java, Go and Python. valkey-py 6.2.0's ValkeyCluster instead raises Argument 'db' is not possible to use in cluster mode (cluster.py). The Valkey blog also warns that pooled clients can hand out a connection with another database still selected. Test each client on staging across a node restart: one that reconnects into database 0 reads and writes the wrong data without an error.
A prefix changes the key name, so it changes the slot, unless the key already has a hash tag such as {user:42}. Keep the prefix outside the braces. Never put the prefix itself inside braces ({db3}:key): that sends a whole database to one slot on one node.
Which migration method fits each database?
Choose per database, not once for the whole instance.
| Method | Databases moved | Writes during copy | Best for |
|---|---|---|---|
valkey-cli --cluster import | Database 0 only | Not captured | Database 0, when nothing else is in use |
| RDB load into one empty cluster node | All, in one file | Stop writes first | Large data, an outage window is acceptable |
| Scripted MIGRATE ... COPY | One per run, same id | Needs a final pass | Keeping databases, moving online in stages |
| Scripted DUMP and RESTORE | One per run, any target name | Needs a final pass | Remapping to prefixes, or targets the source cannot reach |
| Re-ingest or warm from the system of record | Any | Application handles it | Caches that can start cold |
Per-database migration into a Valkey 9 cluster
Each source database lands in the same database number on the cluster, then gets its own count check
Order: move and verify the smallest database first to prove ACLs, routing and client settings.
What does valkey-cli --cluster import actually move?
The cluster tutorial says the command "moves all the keys of a running instance". The valkey-cli 9.1.3 source is narrower: it scans the source without a SELECT, logs *** Importing %d keys from DB 0, and sends MIGRATE with destination database 0 hard-coded. The unstable branch was unchanged as of 7 November 2026.
valkey-cli --cluster import 10.0.1.11:6379 \
--cluster-from 10.0.0.5:6379 \
--cluster-from-user migrator --cluster-from-pass "$SRC_PASS" \
--cluster-copy --cluster-replaceInterpretation: this copies database 0 and keeps the source keys. Without --cluster-copy, each key is deleted from the source once the target accepts it, which removes your rollback copy. The source must not be a cluster node, and the command moves one key per MIGRATE call, so it is slow on large databases.
The command exits successfully after database 0. The other databases simply stay on the source, which is why verification compares every database.
Can I load the standalone RDB file straight into a cluster node?
Yes, with care. In 9.1.3, the loader exits with the FATAL ... more than N databases message if the file uses a database id at or above cluster-databases. After loading, a primary checks every slot holding keys in any database. It takes ownership of unassigned slots (I have keys for unassigned slot N. Taking responsibility for it.), but deletes keys in slots owned by another node (cluster_legacy.c). So load the file into one empty node that owns no slots, then spread the slots out.
# On the source, with application writes stopped
valkey-cli -h 10.0.0.5 BGSAVE
valkey-cli -h 10.0.0.5 LASTSAVE
# New node: cluster-enabled yes, cluster-databases 16, appendonly no,
# no nodes.conf yet, the copied dump.rdb in its dir
valkey-server /etc/valkey/node-a.conf
valkey-cli --cluster fix 10.0.1.11:6379Interpretation: when LASTSAVE changes, the snapshot is complete. The new node claims every slot that has keys, and --cluster fix covers the empty ones after asking for confirmation. Valkey reads RDB files from Redis OSS 7.2 and earlier and from Valkey, but not from Redis CE 7.4 and later (Valkey migration guide).
valkey-cli -h 10.0.1.11 CONFIG SET appendonly yes
valkey-cli --cluster add-node 10.0.1.12:6379 10.0.1.11:6379
valkey-cli --cluster add-node 10.0.1.13:6379 10.0.1.11:6379
valkey-cli --cluster rebalance 10.0.1.11:6379 \
--cluster-use-empty-primaries --cluster-use-atomic-slot-migrationInterpretation: enabling AOF after the load avoids starting from an empty append-only file. rebalance then moves slots to the new primaries with atomic slot migration, which carries every database. The --cluster-use-atomic-slot-migration flag exists in valkey-cli 9.1 and later, not 9.0.7, and the next section covers the patch floor it needs. Add replicas last.
How do I copy databases 1 to 15 with MIGRATE or DUMP and RESTORE?
MIGRATE "operates on the database currently selected in the connection" and takes a destination database (MIGRATE documentation). The source sends SELECT and a RESTORE with the remaining TTL, and the target must own each key's slot. This valkey-py script uses a standalone client for the source database and a ValkeyCluster client only to find slot owners.
# copy_db.py: copy one source database into the same database number on the cluster
import sys
from collections import defaultdict
import valkey
from valkey.cluster import ValkeyCluster
DB = int(sys.argv[1])
USER, PASS = "migrator", sys.argv[2]
src = valkey.Valkey(host="10.0.0.5", port=6379, db=DB, username=USER, password=PASS)
router = ValkeyCluster(host="10.0.1.11", port=6379, username=USER, password=PASS)
cursor = 0
while True:
cursor, keys = src.scan(cursor=cursor, count=500)
batches = defaultdict(list)
for key in keys:
node = router.get_node_from_key(key)
batches[(node.host, node.port)].append(key)
for (host, port), batch in batches.items():
src.execute_command("MIGRATE", host, port, "", DB, 5000,
"COPY", "REPLACE", "AUTH2", USER, PASS, "KEYS", *batch)
if cursor == 0:
breakInterpretation: COPY keeps the source keys, and REPLACE lets you rerun the script as a catch-up pass. NOKEY is normal for keys that expired mid-scan. Target instance replied with error usually means missing cluster-databases headroom, a moved slot, or ACL access. Lower count for large keys, because MIGRATE blocks both instances during each batch.
The target user needs SELECT, RESTORE and access to the database. Database permissions (db=) arrived in 9.1, and SELECT into a forbidden database fails with NOPERM (ACL documentation).
# users.acl, shipped to every cluster node (Valkey 9.1 or later)
user migrator on >CHANGE_ME ~* +@read +@write +select +ping alldbs
user sessions on >CHANGE_ME ~* +@read +@write -@dangerous +select db=3Interpretation: the migrator can write to any database, while sessions can use only database 3, so a misconfigured client fails with NOPERM instead of writing to database 0. ACL users are per node.
To remap a database to a prefix instead, read with DUMP and write with RESTORE under the new name. The DUMP documentation says the payload "does NOT contain expire information", so read PTTL as well.
# remap_db.py: same src and router setup as copy_db.py
# Moves source database DB into cluster database 0 as "db{DB}:key"
for key in src.scan_iter(count=500):
payload = src.dump(key)
if payload is None:
continue # expired or deleted since the scan
ttl = src.pttl(key) # -1 means no expiry
router.restore(f"db{DB}:".encode() + key, max(ttl, 0), payload, replace=True)Interpretation: a TTL of 0 tells RESTORE to create the key without an expiry. Because DUMP is a read-only command, this script also works while the source is under CLIENT PAUSE WRITE, which blocks MIGRATE.
What patch level do I need before resharding with several databases?
Valkey 9.1.3 (26 October 2026) and 9.0.7 (27 October 2026) "Fix atomic slot migration with cluster-databases > 1 applying writes made during the snapshot to the wrong database on the target" (9.1.3 release notes). Per PR #4952, the snapshot selects each database it copies, but the source's parent process never sees those SELECTs, so a write made during the snapshot could run in another database on the target. Both releases also fix unreplicated evictions on the import target (#4920) and are marked as security releases.
valkey-cli --cluster call 10.0.1.11:6379 INFO server \
| grep -o 'valkey_version:[0-9.]*'
valkey-cli -h 10.0.1.11 CONFIG GET cluster-databasesInterpretation: every node must report 9.0.7 or later on 9.0, or 9.1.3 or later on 9.1. At cluster-databases 1 the bug cannot occur. The fix does not repair keys already moved, so check any shard resharded under write load on an older release.
CLUSTER MIGRATESLOTS and CLUSTER CANCELSLOTMIGRATIONS need alldbs, and the primaryuser account needs +select (atomic slot migration documentation). The full runbook is in Resharding Valkey 9 with atomic slot migration.
Without the atomic flag, valkey-cli --cluster reshard moves keys database by database, using CONFIG GET cluster-databases to find how many there are. If that call fails, for example because the ACL user lacks CONFIG, it prints "use default value 1 instead" and moves database 0 only.
How do I verify each database after the move?
On a cluster, DBSIZE and INFO keyspace report one node's share of each database, so add them up across primaries. Do not rely on valkey-cli --cluster check for this: its "keys" figure comes from DBSIZE on a connection in database 0.
# Sum keys per database across all primaries
valkey-cli --cluster call 10.0.1.11:6379 INFO keyspace --cluster-only-primaries \
| grep -o 'db[0-9]*:keys=[0-9]*' \
| awk -F'[:=]' '{sum[$1] += $3} END {for (d in sum) print d, "keys=" sum[d]}' \
| sort -VInterpretation: compare each line with the source's INFO keyspace. With writes stopped, the counts should match exactly, except for keys that expired in between. A database present on the source and missing here was never copied.
INFO keyspace on the source, then summed across the cluster
Illustrative output from an 8.1 source and a 9.1 cluster: database 3 is 1,200 keys short after the first pass
Next step: rerun the copy script for database 3 with REPLACE and compare again. Lines follow the 8.1.11 format; 9.x nodes add a keys_with_volatile_items field.
Counts catch missing databases, not wrong values. Also spot-check a random sample of keys in each database: TYPE must match on both sides and PTTL on the cluster should be equal or slightly lower. With valkey-cli -c -n 3, the CLI follows redirects and selects database 3 again on each new connection.
How do I cut clients over and keep a way back?
Copy in bulk while the source serves traffic, then stop writes for a short final pass. The scripts do not remove keys deleted on the source after they were copied, so for exact parity run the whole copy inside the freeze, or finish by deleting cluster keys that no longer exist on the source.
Runbook: multi-database instance into a Valkey 9.x cluster
Eight steps; steps 5 to 7 are the write outage window
-
Inventory databases and clients
Record keys and expires per database, and map each database to the services that use it.
valkey-cli -h 10.0.0.5 INFO keyspace -
Build the cluster with cluster-databases set
Run 9.0.7 or 9.1.3 or later on every node, and set the value above the highest database id before first start.
cluster-enabled yes cluster-databases 16 -
Ship ACLs and test every client
Give each application user its
db=rule. Prove each client selects its database in cluster mode, across a node restart. -
Bulk copy each database
Smallest database first. Keep source keys with
COPY, and check counts after each database.python3 copy_db.py 1 "$PASS" python3 copy_db.py 3 "$PASS" -
Stop writes on the source
Outage startsStop writers in the application.
CLIENT PAUSE WRITEalso blocks MIGRATE, so use it only with the DUMP and RESTORE script. -
Run the final pass and verify
OutageCopy again with
REPLACE, remove keys deleted since the bulk copy, then compare the summed counts per database. -
Repoint clients to the cluster
Outage endsDeploy the cluster client configuration with each service's database id, and watch
ACL LOGfor denials.valkey-cli -h 10.0.1.11 ACL LOG 10 -
Keep the source frozen for the rollback window
Leave it running without writers until the agreed window ends.
Rollback point: until the first write reaches the cluster in step 7, rollback is repointing clients to the untouched source. After that, you need a reverse copy with the DUMP and RESTORE script.
One more limit: a Valkey 8.x cluster refuses keys outside database 0 (its 8.1.11 startup check fails), so the way back is to the standalone source or a 9.x cluster. The Valkey 9.1 upgrade guide lists the other 9.x features that block a return to 8.x.
Before you retire the source, confirm its INFO keyspace still matches the step 1 baseline and CLIENT LIST shows no application writers.
Frequently asked questions
Can Valkey Cluster use SELECT and multiple databases?
Yes, from Valkey 9.0. Set cluster-databases in the config of every node before it starts, because the default is 1 and the value cannot change at runtime. SELECT, MOVE and COPY with a database work. SWAPDB is rejected in cluster mode, and FLUSHDB, SCAN and DBSIZE act only on the node you are connected to.
Does valkey-cli --cluster import copy all databases?
No. In Valkey 9.1.3 it scans database 0 on the source and writes to database 0 on the cluster, and it logs "Importing N keys from DB 0". It also deletes each source key after copying it unless you pass --cluster-copy. Move databases 1 and up with a MIGRATE or DUMP and RESTORE script, or by loading the RDB file into one empty cluster node.
Which Valkey version is safe for atomic slot migration with several databases?
Valkey 9.0.7 or 9.1.3, or a later patch, on every node. Earlier 9.x releases could apply writes made during the migration snapshot to the wrong database on the target when cluster-databases was above 1. Both releases also fix unreplicated evictions on the import target and carry security fixes.
Should I keep numbered databases or switch to key prefixes?
Keep databases if every client library can select a non-zero database in cluster mode and you do not need to move back to an 8.x cluster. Switch to prefixes if a client cannot, or if you need to stay compatible with older clusters. Prefixes cost memory on every key and change slots unless the key has a hash tag.
Key takeaways
- Size
cluster-databasesto the highest database id in use and set it before first start; it cannot change at runtime. --cluster importmoves database 0 only and deletes source keys without--cluster-copy, so script the other databases or load the RDB into one empty node.- Run 9.0.7 or 9.1.3 or later before any atomic slot migration with more than one database.
- Test every client's database selection in cluster mode, including reconnects and connection pools.
- Verify each database by summing
INFO keyspaceacross primaries, and keep the source untouched until the rollback window closes.
Planning a multi-database Valkey migration with JusDB
The risk in this move sits in details: a client that falls back to database 0, a tool that copies one database and reports success, or a patch level older than the multi-database fixes. Rehearse each database's path on staging.
JusDB's Valkey migration team can review your database inventory, client libraries and patch levels, plan the method for each database, and help run the cutover and rollback checks with your team.