Most teams use valkey-cli the way they used redis-cli: -h, -p, -a and a command. That habit puts the password in the process list, sends KEYS * to a busy primary, and lets a shell script carry on after an (error) NOAUTH Authentication required. reply, because valkey-cli exits 0 on an error reply unless you pass -e. Valkey 9.0 added VALKEYCLI_* environment variables, 9.1 added a hidden atomic slot migration flag to --cluster, and the 9.2 release candidates add more. This post walks through the options an operator should know in valkey-cli 9.1.3: environment variables, valkeys:// URIs, --json, remote RDB transfers, key and latency scans, the cluster manager, and the modes that are risky on a production primary.
- Pass the password with
VALKEYCLI_AUTH(9.0 and later;REDISCLI_AUTHstill works as a fallback) and setVALKEYCLI_HISTFILE=/dev/nullon shared hosts. - In scripts, always add
-e. Without it, error replies exit 0, and--jsonprints errors aserror:"...", which is not valid JSON. --jsondoes not changeINFO,CLIENT LISTorCLUSTER NODES: those always print raw text.--rdb,--functions-rdband--replicaeach make the server fork. Point them at a replica.--cluster callexits 0 even when nodes fail,--cluster importmoves DB 0 only and deletes source keys unless you pass--cluster-copy, and--cluster backupis not one point in time.
Scope and review date: November 2026. This post covers valkey-cli 9.1.3 (released 26 October 2026), with notes on 8.1.11, 9.0.7 and the 9.2.0-rc2 release candidate. Options and behaviour were checked against the valkey-cli documentation, the release notes, and the valkey-cli.c source at each tag on 10 November 2026. The commands were not run in a lab, so try them on staging first.
Which valkey-cli features depend on the version you run?
The client version decides which options exist, not the server version. A 9.1.3 valkey-cli can talk to an 8.1 server, but an 8.1 valkey-cli on a bastion host does not read VALKEYCLI_AUTH. Check the binary on every host your scripts run from.
valkey-cli --version
valkey-cli --help | grep -E 'VALKEYCLI|json|functions-rdb'Interpretation: --version prints the client build. If the help text names REDISCLI_AUTH instead of VALKEYCLI_AUTH, you have an 8.x client.
| Feature | First release | Source |
|---|---|---|
| Re-selects the database after a reconnect; leaves subscribed mode when no subscriptions remain | 8.1.0 | 8.1.0 release notes |
VALKEYCLI_AUTH, VALKEYCLI_HISTFILE, VALKEYCLI_RCFILE, VALKEYCLI_CLUSTER_YES, with REDISCLI_* fallback | 9.0.0 | #1995, #2334 |
--hotkeys-count, --rdma, --mptcp | 9.0.0 | #1933, #2059, #2067 |
--cluster del-node removes unreachable nodes | 7.2.14, 8.0.10, 8.1.8, 9.0.5, 9.1.0 | #3209 |
--cluster-use-atomic-slot-migration | 9.1.0 | #2755 |
VALKEYCLI_USER, VALKEYCLI_HOST, VALKEYCLI_PORT | 9.2.0-rc1 (release candidate) | #4126, #3402 |
--rdb exits non-zero when trimming the EOF marker fails | 9.2.0-rc1 (release candidate) | #3945 |
The 9.0.0-rc1 release notes and the 9.2.0-rc1 release notes list these changes. As of November 2026 the documentation page shows VALKEYCLI_HOST and VALKEYCLI_PORT in the option defaults with no version label, but the 9.1.3 source does not read them. Treat the documentation as describing 9.2.
Old redis-cli habits and the valkey-cli option to use instead
Switch panels to compare each habit with the 9.1 option that replaces it
Old habit
| Task | What teams type |
|---|---|
| Authenticate | -a 'secret' on the command line |
| Find keys by pattern | KEYS 'session:*' |
| Parse a reply in a script | grep and awk on formatted output |
| Fail a script on an error reply | Check for (error) in the output |
| Run one command on every node | A shell loop over a hand-kept host list |
| Copy a snapshot off the server | scp of dump.rdb from the data directory |
Better valkey-cli option
| Task | What teams type |
|---|---|
| Authenticate | VALKEYCLI_AUTH or --askpass |
| Find keys by pattern | --scan --pattern 'session:*' |
| Parse a reply in a script | --json piped to jq |
| Fail a script on an error reply | -e: the error goes to stderr and the exit code is 1 |
| Run one command on every node | --cluster call host:port CMD |
| Copy a snapshot off the server | --rdb file against a replica, then valkey-check-rdb |
Every row has a trap: --json skips some commands, --cluster call ignores failures in its exit code, and --rdb forks the server. The sections below cover each one.
How do you keep the Valkey password off the command line?
With -a, the password is visible in ps output, in shell history, and in CI logs, and valkey-cli prints Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. From 9.0, valkey-cli reads VALKEYCLI_AUTH and falls back to REDISCLI_AUTH. In 9.1.3 the environment is read after the options, and it only fills the password if -a, --pass or -u did not set one, so a command-line password still wins.
# Mixed 8.x and 9.x clients: REDISCLI_AUTH works on both
export REDISCLI_AUTH="$(cat /run/secrets/valkey-monitor)"
export VALKEYCLI_HISTFILE=/dev/null # no history file on a shared bastion
valkey-cli --user monitor -h 10.0.1.11 PINGInterpretation: PONG means the ACL user authenticated. On 9.1, the user name still comes from --user; VALKEYCLI_USER exists only in the 9.2 release candidates. On 8.1.11, only the REDISCLI_* names exist, which is why the fallback name is the safe choice for a mixed fleet.
--askpass prompts for the password and ignores both -a and the environment variable.
History needs its own attention. Since 8.0, valkey-cli keeps all commands in memory for the session, but it does not write sensitive commands to ~/.valkeycli_history. In the 9.1.3 source these include AUTH, ACL SETUSER and CONFIG SET requirepass. A SET of a customer record still lands in the file, so setting the history file to /dev/null is the simple fix on shared hosts.
How do valkeys:// URIs work?
-u accepts valkey://, valkeys://, redis:// and rediss://, in the form scheme://[user:password@]host[:port][/db]. The valkeys and rediss schemes turn on TLS. Query parameters are not supported, so CA and client certificate paths still go in flags.
export VALKEYCLI_AUTH="$(cat /run/secrets/valkey-ops)"
valkey-cli -u valkeys://valkey-prod.internal:6380/0 \
--cacert /etc/valkey/tls/ca.crt --user ops PINGInterpretation: the URI carries no secret, so the password stays in the environment. Two traps come from the parser in cli_common.c:
- A user name with no colon is read as the password.
valkeys://ops@hostsendsAUTH ops. Use--user opsinstead. - Special characters must be percent-encoded. The parser decodes
%40for@and%3Afor:in the user and password parts.
A valkey-cli built without TLS rejects the scheme with valkeys:// is only supported when valkey-cli is compiled with OpenSSL.
How do you get scriptable output from valkey-cli with --json?
--json asks for RESP3 with HELLO 3 and converts the reply: maps become JSON objects, arrays and sets become arrays, nil becomes null, and RESP3 booleans become true or false. If HELLO 3 fails, the client prints the error and carries on in RESP2. Pass -2 to force RESP2, which turns maps such as the CONFIG GET reply into flat arrays. --quoted-json escapes non-ASCII and binary bytes, which matters when values are not UTF-8.
valkey-cli -e --json CONFIG GET maxmemory maxmemory-policy \
| jq -r '.maxmemory'Interpretation: in RESP3, CONFIG GET returns a map, so jq can read each parameter by name. A value of "0" for maxmemory means no memory limit is set.
Two behaviours from the 9.1.3 source break naive scripts:
- Some commands always print raw text.
INFO,CLUSTER NODES,CLUSTER INFO,CLIENT LIST,CLIENT INFO,MEMORY DOCTOR,LATENCY DOCTORand a fewDEBUGsubcommands bypass the output format, even with--json. ParseINFOwithgrepand strip the\r. - Errors are not JSON. Without
-e, an error reply prints aserror:"WRONGTYPE ..."on stdout, and the exit code is 0. With-e, the error text goes to stderr and the exit code is 1.
# INFO is raw text even with --json: strip the CR before comparing
valkey-cli -e INFO memory | tr -d '\r' \
| awk -F: '$1 == "mem_fragmentation_ratio" { print $2 }'Interpretation: this prints one number. Read it with used_memory in mind: a high ratio on a nearly empty instance means little, while a rising ratio on a large dataset can justify active defragmentation.
valkey-cli --json on four kinds of reply
Illustrative output, shaped like valkey-cli 9.1.3: a map, an array through jq, raw INFO, and an error
Read the last two: INFO ignores --json, and an error without -e is not valid JSON and exits 0. The formatter is in valkey-cli.c at 9.1.3.
How do you script a Valkey health check with valkey-cli?
Combine authentication from the environment, -e on every call, --json for structured replies, and text parsing for INFO. Run the script with set -euo pipefail so a failure inside a pipe stops it.
Valkey keeps the master and slave strings in ROLE and INFO for compatibility, so match those words, not "primary".
A scripted health check with valkey-cli 9.1
Six read-only steps; each one exits non-zero on failure so cron or CI can alert
-
Load credentials without flags
Use the fallback variable name so 8.x and 9.x clients both read it, and turn off the history file.
export REDISCLI_AUTH="$(cat /run/secrets/vk)" export VALKEYCLI_HISTFILE=/dev/null -
Check reachability and authentication
With -e, a NOAUTH or WRONGPASS reply exits 1 instead of printing an error and exiting 0.
valkey-cli -e --user monitor -h 10.0.1.11 PING -
Confirm the role and replica count
Compare with the topology you expect for this node.
valkey-cli -e --json ROLE \ | jq -e '.[0] == "master" and (.[2]|length) >= 1' -
Check the memory limit and policy
A maxmemory of 0 on a cache means it can grow until the host runs out of memory.
valkey-cli -e --json CONFIG GET maxmemory \ | jq -e '.maxmemory != "0"' -
Sample PING latency for 5 seconds
Outside a terminal, --latency samples once and exits; -i sets the sample length in seconds.
valkey-cli --latency --json -i 5 \ | jq -e '.max < 20' -
Check cluster slot coverage
--cluster check exits 1 when slots are open, uncovered or disputed.
valkey-cli --user monitor \ --cluster check 10.0.1.11:6379
Main risk: jq -e turns a false check into exit code 1, but only -e on valkey-cli catches error replies. The 20 ms threshold is a placeholder; set it from your own baseline.
How do you take a remote backup with valkey-cli --rdb?
--rdb <file> connects as a replica, sends REPLCONF capa eof and REPLCONF rdb-only 1, then SYNC, and writes the snapshot it receives to the file (- means stdout). --functions-rdb <file> also sends REPLCONF rdb-filter-only functions, so the file holds only the function libraries. The two options cannot be combined.
ts=$(date +%Y%m%dT%H%M)
valkey-cli --user backup -h 10.0.2.12 --rdb "/backup/vk-$ts.rdb.tmp" \
&& valkey-check-rdb "/backup/vk-$ts.rdb.tmp" \
&& mv "/backup/vk-$ts.rdb.tmp" "/backup/vk-$ts.rdb"Interpretation: the file is renamed only if the transfer and the check both exit 0. valkey-check-rdb prints \o/ RDB looks OK! \o/ and exits 0 on a good file. Write to a new file each time: in 9.1.3 the client opens the target without truncating it.
Three things to know before you put this in cron:
- It forks the server. The source node runs a background save for this "replica", either to disk or straight to the socket. A filtered request such as
--functions-rdbalways uses the socket path. That is the same fork and copy-on-write memory cost as a full resync, so run it against a replica, here10.0.2.12. - The ACL user needs
SYNCandREPLCONF. Both are in the@adminand@dangerouscategories in 9.1.3, so give a dedicated backup user only those two commands. - Check the exit code, and on 9.1 check the file too. The documentation says a non-zero exit means the transfer failed. In 9.1.3, if trimming the EOF marker from a diskless transfer fails, the client prints
ftruncate failedand still reports success and exits 0. PR #3945 makes that exit 1 in 9.2.0-rc1. Until you run 9.2,valkey-check-rdbis what catches it.
For full backup and restore drills, including multi-part AOF and cluster rebuilds, see Valkey backups and restore drills.
How do you find big, heavy, and hot keys without KEYS?
The KEYS command page says to use KEYS in production "with extreme care" because it can stall the server on a large database. valkey-cli has four SCAN-based modes instead. All of them scan only the node you connect to, so in a cluster run them against each shard, ideally on a replica.
valkey-cli -h 10.0.2.12 --scan --pattern 'session:*' --count 1000 | wc -l
valkey-cli -h 10.0.2.12 --bigkeys -i 0.01
valkey-cli -h 10.0.2.12 --memkeys --memkeys-samples 100 -i 0.01
valkey-cli -h 10.0.2.12 --hotkeys-count 32Interpretation:
--scanmay print a key more than once if the data changes during the scan, but it does not miss a key that existed for the whole scan. Deduplicate before you count exactly.--bigkeysreports the largest key of each type by element count (STRLEN,LLEN,HLENand so on).--memkeysusesMEMORY USAGEinstead, so a hash with few fields but large values shows up there and not in--bigkeys.--hotkeysreadsOBJECT FREQand works only whenmaxmemory-policyis an LFU policy. On any other policy it exits with the server's error.-i 0.01sleeps 10 ms per 100 SCAN calls in these modes, which lowers the load on a busy node.
There is no --keystats option in valkey-cli 9.1.3 or 9.2.0-rc2; the parser rejects it with Unrecognized option. Server-side hot key tracking (the HOTKEYS command) is new in 9.2 and exists only in the release candidates as of November 2026.
How do you read --latency, --latency-history and --intrinsic-latency?
--latency sends PING about every 10 ms and reports the minimum, maximum and average round trip in milliseconds, measured with a millisecond clock. A reading of min: 0 is normal: it means under 1 ms, not zero. In a terminal it runs until you stop it. With output redirected, --raw, --csv or --json, it samples once (1 second by default, or the -i value) and exits, which is what the health check above uses.
valkey-cli -h 10.0.1.11 --latency-history -i 30
valkey-cli -h 10.0.1.11 --latency --json -i 10Interpretation: --latency-history resets the statistics every 15 seconds by default (here every 30) and prints one line per window, such as min: 0, max: 3, avg: 0.21 (2850 samples) -- 30.01 seconds range. A window whose max jumps while avg stays flat points to a short stall, such as a fork, an expiry burst or a slow command. The JSON form prints {"min": ..., "max": ..., "avg": ..., "count": ...}.
--intrinsic-latency <seconds> is different: it does not connect to Valkey at all. It runs a tight loop on the local machine and reports the worst scheduling delay in microseconds. Run it on the Valkey host itself, as the documentation says, while the host is under its normal load.
valkey-cli --intrinsic-latency 60Interpretation: the last two lines give the total runs, the average per run, and how many times longer the worst run took. If the worst run is several milliseconds, Valkey cannot respond faster than that on this host, whatever you tune in the server. For a VM baseline built on this, see running Valkey on VMs. For an incident workflow, see the Valkey latency spike runbook.
What do the valkey-cli --cluster helpers really do?
The cluster manager connects to one node, reads its view of the cluster, and then connects to every node it lists. Each subcommand has behaviour the help text does not mention. Old master option names still work beside the primary forms.
check and fix
valkey-cli --cluster check 10.0.1.11:6379 --cluster-search-multiple-owners
echo "exit=$?"Interpretation: a healthy cluster prints [OK] All nodes agree about slots configuration. and [OK] All 16384 slots covered. and exits 0. Any open, uncovered or disputed slot makes it exit 1, so it is safe to use in monitoring.
--cluster fix changes slot ownership. Its slot-coverage prompts are hard-coded to ignore --cluster-yes and VALKEYCLI_CLUSTER_YES=1, so someone must type yes at each one. Since 9.0.5 (and 8.0.10, 7.2.14, and every 9.1 release), it assigns each uncovered, empty slot to a random primary instead of giving them all to one node. With --cluster-fix-with-unreachable-primaries, it assumes the unreachable primaries' slots are lost. Read what it proposes before you agree.
call
valkey-cli --user ops --cluster call 10.0.1.11:6379 \
CONFIG GET maxmemory --cluster-only-primaries 2>call.err
echo "exit=$?"; cat call.errInterpretation: stdout has one line per node, in the form 10.0.1.11:6379: maxmemory followed by the value on the next line. The reply always uses the raw format, so --json has no effect here.
How valkey-cli --cluster call fans out
Follow the numbers; the dashed path is the one a script misses
Exit code 0 is not success: in 9.1.3, --cluster call returns 0 unless it cannot read the topology. Unreachable nodes go to stderr and failed replies print Failed!, so check both streams.
backup, import, and atomic slot migration
--cluster backup host:port /dir pulls an RDB from each primary in turn, using the same transfer as --rdb, into files named valkey-node-<ip>-<port>-<node-id>.rdb, and writes the topology to nodes.json. The primaries are snapshotted one after another, so the set is not a single point in time, and each primary forks. If the cluster check finds problems, it still writes the backup and warns.
--cluster import copies a standalone instance into a cluster with SCAN and MIGRATE, and it has three traps in the 9.1.3 source:
- It reads only DB 0 of the source and logs
*** Importing N keys from DB 0. Keys in other databases are left behind. - Without
--cluster-copy,MIGRATEdeletes each key from the source after copying it. Add--cluster-replaceonly if overwriting target keys is acceptable. - The source must not be a cluster node, and the first failed
MIGRATEstops the import partway.
valkey-cli --user ops --cluster import 10.0.1.11:6379 \
--cluster-from 10.0.9.5:6379 --cluster-from-user migrator \
--cluster-from-askpass --cluster-copyInterpretation: with --cluster-copy, the source keeps its data, so you can compare key counts and retry. The cluster password comes from VALKEYCLI_AUTH, and the source password is asked for interactively.
From 9.1.0, --cluster reshard and --cluster rebalance accept --cluster-use-atomic-slot-migration (PR #2755 in the 9.1.0-rc1 notes), which moves slot ranges with CLUSTER MIGRATESLOTS instead of key-by-key MIGRATE. The flag is missing from --cluster help in both 9.1.3 and 9.2.0-rc2, and --cluster fix never uses it. The atomic slot migration runbook covers when to use it.
When are --replica, --pipe, --stat and MONITOR safe in production?
--replica prints the replication stream as CSV, which is useful when you suspect a write is not replicating. It requests an RDB with data and functions filtered out, so the server still forks to produce it, and the client then appears as a replica in INFO replication. Run it on a replica of the node you are debugging, and stop it when you are done.
--pipe streams raw RESP from stdin for bulk loads, as described in the mass insertion guide. It ends with an ECHO marker, prints errors: N, replies: M, and exits 1 if any reply was an error.
./gen-resp.py | valkey-cli -h 10.0.1.11 --pipe --pipe-timeout 60 2>load.err
echo "exit=$?"; head load.errInterpretation: each error reply is written to stderr. In a cluster, keys for slots owned by other nodes come back as MOVED errors, so pipe each node only the keys it owns. Since 9.1.0 (and 9.0.5, 8.1.8, 8.0.10, 7.2.14), the server strictly checks CRLF terminators, so a generator that writes a bare \n now gets a protocol error.
--stat prints one INFO-based line per second. Its connections column is total_connections_received, a running total, so a fast-growing number means connection churn.
The MONITOR page shows a single MONITOR client cutting benchmark throughput by more than 50%. It also prints every key and value, including personal data, into your terminal and scrollback; only AUTH is redacted. Prefer COMMANDLOG or SLOWLOG, and if you must use MONITOR, run it for seconds, filtered with grep, on a node you can afford to slow down.
Frequently asked questions
How do I pass a password to valkey-cli without -a?
Set the VALKEYCLI_AUTH environment variable, which valkey-cli reads from version 9.0. It falls back to REDISCLI_AUTH, which also works on 8.x clients, so that name suits mixed fleets. A password given with -a or in a -u URI takes precedence. For interactive sessions, --askpass prompts for the password and ignores both. Pass the ACL user name with --user on 9.1.
Why does valkey-cli --json not return JSON for INFO?
valkey-cli prints INFO, CLIENT LIST, CLIENT INFO, CLUSTER NODES, CLUSTER INFO and a few doctor commands in raw format whatever output mode you choose. That is hard-coded in the client. Parse INFO with grep or awk and strip the carriage returns. Use --json for commands that return structured replies, such as CONFIG GET, ROLE, CLUSTER SHARDS and HGETALL.
Is valkey-cli --rdb safe to run against a production primary?
It works, but it makes the server fork a background save, the same cost as a replica full resync, including copy-on-write memory. Run it against a replica instead. Check the exit code, and on 9.1 also run valkey-check-rdb on the file, because one failure path still exits 0 until the 9.2 fix in PR 3945.
Does valkey-cli --cluster import delete keys from the source?
Yes, unless you add --cluster-copy. The import uses MIGRATE for each key, and MIGRATE without COPY removes the key from the source after it reaches the cluster. It also reads only database 0 of the source, and the source must be a standalone instance, not a cluster node.
Does valkey-cli have a --keystats option?
No. Neither valkey-cli 9.1.3 nor 9.2.0-rc2 has a --keystats option, and the client rejects it as unrecognized. Use --bigkeys for the largest key per type by element count, --memkeys for memory use per key, and --hotkeys with an LFU eviction policy. Server-side hot key tracking arrives with the HOTKEYS command in the 9.2 release candidates.
Key takeaways
- Check the client version on every host: VALKEYCLI_* variables need 9.0, the atomic slot migration flag needs 9.1, and VALKEYCLI_USER, HOST and PORT are 9.2 release candidate only.
- Keep secrets in VALKEYCLI_AUTH or REDISCLI_AUTH, use --user rather than user@ in a URI, and set VALKEYCLI_HISTFILE=/dev/null on shared hosts.
- Add -e to every scripted call, use --json only for structured replies, and parse INFO as text.
- Run --rdb, --functions-rdb, --replica and the key scans against replicas, and verify each RDB with valkey-check-rdb.
- Do not trust the exit code of --cluster call, add --cluster-copy to --cluster import, and treat --cluster backup as per-shard snapshots taken at different times.
Run Valkey operations with JusDB
JusDB's Valkey remote DBA team can review your operational scripts and runbooks, set up replica-offloaded backups and restore drills, and plan cluster changes such as resharding or node replacement.
Bring your Valkey versions, topology, and the scripts your team runs today. Your team approves every production change.