Skip to content

Using redis-cli

Verified on Redis 8.10.2 (redis:8.10.2-alpine, redis-cli 8.10.2 from the same image), on an Apple-silicon Mac. Outputs are real; where the output depends on whether a terminal is attached, the lesson says which.

redis-cli is the command-line interface bundled with every Redis installation (and available inside the official Docker image). It supports two modes: interactive (a live REPL prompt) and inline (a single command passed directly from your shell).

The most common flags:

FlagDefaultPurpose
-h host127.0.0.1Redis server hostname or IP
-p port6379Redis server port
-n db-number0Select a database number (0–15) on connect
-u uri—One URI instead of three flags: redis://user:pass@host:6379/1 (rediss:// for TLS)
-3RESP2Speak RESP3 (same as sending HELLO 3 first)
scripts/intro-redis-cli-connect.sh
# Connect to the default host and port
redis-cli
# Connect explicitly — useful when targeting a remote server
redis-cli -h 127.0.0.1 -p 6379
# Connect and immediately switch to database 1
redis-cli -n 1
# The same thing as one URI; -u redis://127.0.0.1:6379/1 DBSIZE ran against the real server and printed 0
redis-cli -u redis://127.0.0.1:6379/1 DBSIZE

In inline mode you pass a single command as arguments to redis-cli and the result is printed to stdout — handy for scripts:

scripts/intro-redis-cli-inline.sh
redis-cli PING

In interactive mode you run redis-cli with no command arguments and get the 127.0.0.1:6379> prompt where you can type commands one by one.

The same reply prints differently depending on where it goes

Section titled “The same reply prints differently depending on where it goes”

This is the single most useful thing to know about redis-cli, and the source of many broken shell scripts. When stdout is a terminal, redis-cli pretty-prints replies ((nil), (integer) 0, quotes, numbered lists). When stdout is not a terminal (a pipe, $(...), a file, an argument-mode call inside a script) it prints raw values instead. Same server, same commands:

# a terminal, or --no-raw # inside a pipe / $(...) / script (default)
"v1" <- GET k1 v1
(nil) <- GET nokey (an empty line)
(integer) 0 <- DEL nokey 0
1) "v1" v1
2) (nil) <- MGET k1 nokey (an empty line)

You can force either way: --no-raw gives the pretty form even when piped, --raw gives the raw form even on a terminal. (The course’s verification harness uses --no-raw so that its transcripts match what you see at the prompt.) Two more output modes worth knowing:

$ redis-cli RPUSH list a "b c" d # then:
$ redis-cli --csv LRANGE list 0 -1
"a","b c","d"

PING is the lightest possible check that the server is reachable:

cli/intro-redis-cli-1.redis
127.0.0.1:6379> PING
PONG

Pass an optional message and Redis echoes it back — useful for testing round-trip latency in scripts:

cli/intro-redis-cli-2.redis
127.0.0.1:6379> PING "hello world"
"hello world"

ECHO returns the argument unchanged. It is mainly useful for testing that the client encodes strings correctly:

cli/intro-redis-cli-3.redis
127.0.0.1:6379> ECHO "Hello"
"Hello"

Redis provides 16 logical databases numbered 0 to 15. They share the same server process and memory but have completely separate keyspaces. The default is database 0. Continue the same session:

cli/intro-redis-cli-4.redis
127.0.0.1:6379> SELECT 1
OK
127.0.0.1:6379[1]> SET greeting "hi from db1"
OK
127.0.0.1:6379[1]> SELECT 0
OK
127.0.0.1:6379> GET greeting
(nil)

Notice the prompt changes to 127.0.0.1:6379[1] when you are on a database other than 0.

DBSIZE returns the number of keys in the currently selected database. The session so far only wrote to database 1, so database 0 is empty:

cli/intro-redis-cli-5.redis
127.0.0.1:6379> DBSIZE
(integer) 0
127.0.0.1:6379> SET a 1
OK
127.0.0.1:6379> SET b 2
OK
127.0.0.1:6379> DBSIZE
(integer) 2

FLUSHDB deletes every key in the current database. This is irreversible — use it only in development or test environments. It also takes ASYNC (free memory in the background) and SYNC:

cli/intro-redis-cli-6.redis
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> DBSIZE
(integer) 0
127.0.0.1:6379> FLUSHDB ASYNC
OK

FLUSHALL clears every database, not just the current one: the greeting key in database 1 survived the FLUSHDB above (it only cleared database 0) and would die only to FLUSHALL.

redis-cli ships with inline documentation. Use HELP followed by a command name or a category tag. HELP SET at the interactive prompt printed (real, from 8.10.2):

SET key value [NX|XX|IFEQ ifeq-value|IFNE ifne-value|IFDEQ ifdeq-digest|IFDNE ifdne-digest] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]
summary: Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.
since: 1.0.0
group: string

The IFEQ|IFNE|IFDEQ|IFDNE options are new in Redis 8.4 — a stale redis-cli on your laptop, talking to a new server, will not autocomplete them. HELP @string lists every command in the string group (it starts with APPEND, DECR, DECRBY, DELEX, DIGEST, GET, GETDEL, GETEX, …), each with its arguments and since: version — a fast way to find out whether a command exists on the server you are running.

PING
ECHO "Hello"
SELECT 1
DBSIZE
SELECT 0
scripts/intro-redis-cli-flags.sh
redis-cli -r 3 -i 0.2 INCR hits # repeat 3 times, 0.2 s apart -> 1 2 3
echo -n "from stdin" | redis-cli -x SET note # -x: last argument read from stdin
redis-cli -e LPUSH note q # -e: exit code 1 on an error reply
redis-cli --scan --pattern 'user:*' # SCAN in a loop, never KEYS (next module)
redis-cli --latency # live min/avg/max round-trip until Ctrl-C
printf 'SET a 1\r\nINCR a\r\nLPUSH a x\r\n' | redis-cli --pipe # mass insert

Real results: -r 3 -i 0.2 INCR printed 1 2 3. -x SET note then GET note returned from stdin. -e LPUSH on that string key printed WRONGTYPE Operation against a key holding the wrong kind of value and exited with 1; the same command without -e exits 0 — so a shell script that ignores this never notices the failure. --latency printed 0.059 1.396 0.143 89 (min, max, avg ms, samples) after 3 seconds on the container. And --pipe fed three commands, the third being a deliberate wrong-type error:

WRONGTYPE Operation against a key holding the wrong kind of value
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 1, replies: 3

--pipe is the protocol-level bulk loader you meet again in the Pipelines lesson. Note the \r\n line endings: it is speaking raw RESP inline commands.

HELLO 3 switches the connection to RESP3 and returns what the server is:

127.0.0.1:6379> HELLO 3
1# "server" => "redis"
2# "version" => "8.10.2"
3# "proto" => (integer) 3
4# "id" => (integer) 43
...

(Trimmed: the reply continues with mode, role and the modules array; your id will differ.) RESP3 gives typed replies — maps stay maps, HGETALL on a missing key is an empty map instead of an empty array — and lets a subscribed connection also run normal commands (the Pub/Sub lesson shows why that matters). The course uses the default RESP2 in the CLI; ioredis 6 negotiates with HELLO on connect.

An assistant writes a health-check script for a deploy pipeline:

🐛 Spot the bug

#!/bin/bash
# fail the deploy if the session key is missing
val=$(redis-cli GET session:42)
if [ "$val" = "(nil)" ]; then
echo "no session" >&2
exit 1
fi
echo "session ok"
Show the answer

The (nil) string only appears when redis-cli writes to a terminal. Inside $(...) stdout is a pipe, so redis-cli prints raw output — for a missing key that is an empty line. We ran it against a missing key: the script printed the “session found” branch with [], i.e. it silently passed. The fix is to test for what a script really receives, or better, ask a question with an unambiguous integer answer:

scripts/intro-redis-cli-check.sh
#!/bin/bash
# EXISTS returns 1 or 0 in raw mode; -e fails the script on server errors
n=$(redis-cli -e EXISTS session:42) || exit 2
if [ "$n" -eq 0 ]; then
echo "no session" >&2
exit 1
fi
echo "session ok"

With the key missing the first script printed session found: [], and [ -z "$val" ] or the EXISTS check both printed “no session” — the fix works.

redis-cli is a protocol client with two personalities: a human one (terminal — labels, quotes, (nil)) and a machine one (pipe — bare values, empty lines). Everything you paste from a transcript into a script has to be re-read in the machine dialect. It is also a thin client: no retries, no pooling, no types — it is for looking at Redis, not for talking to it from an app.

ChoiceBenefitCost
Use redis-cli interactivelyFast to test commands, immediate feedback, great for debuggingNo connection pooling, retries, or error handling that a real app needs
Use a client library in application codeHandles connection lifecycle, pipelining, and retries automaticallyMore code to write, an extra library dependency
Switch databases with SELECTConvenient for separating scratch data during devNot real multi-tenancy — every database still shares the same instance, memory, and performance
Parse redis-cli output in shell scriptsNo dependencies beyond the CLIOutput differs between terminal and pipe; error replies exit 0 unless you pass -e
  • “Whatever I see at the prompt is what $(redis-cli ...) returns.” No: (nil), (integer), quotes and numbering disappear when stdout is not a terminal.
  • “A failed command makes redis-cli exit non-zero.” Only with -e; the WRONGTYPE example exits 0 without it.
  • “SELECT gives me separate Redis servers.” Same process, same memory, same FLUSHALL.
  • “-a is the safe way to pass a password.” It leaks into the process list; use REDISCLI_AUTH or -u from a secret store.
  • Connecting redis-cli directly to production to run fix-up commands — a typo can turn into a FLUSHDB or DEL against real keys. Use a reviewed script or a client library instead when touching production data.
  • Assuming SELECT is safe tenant isolation — the 16 logical databases still share CPU, memory, and a single FLUSHALL affects all of them at once. It is not a substitute for real per-tenant isolation.
  • Treating redis-cli as the only way application code talks to Redis — the healthy pattern is redis-cli for debugging/exploration only, with production code going through a client library that manages connection pooling and retries.
Which flag selects a specific Redis database when connecting with redis-cli?
What does PING return when the server is reachable?
What does FLUSHDB do?
How do you list all commands in the string group from within redis-cli?
In a bash script, val=$(redis-cli GET missing) — what is $val?
A command in a script fails with WRONGTYPE. What is the exit code of redis-cli LPUSH str x, and of redis-cli -e LPUSH str x?