Using redis-cli
Verified on Redis 8.10.2 (
redis:8.10.2-alpine,redis-cli8.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 — the standard Redis client
Section titled “redis-cli — the standard Redis client”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).
Connecting options
Section titled “Connecting options”The most common flags:
| Flag | Default | Purpose |
|---|---|---|
-h host | 127.0.0.1 | Redis server hostname or IP |
-p port | 6379 | Redis server port |
-n db-number | 0 | Select a database number (0–15) on connect |
-u uri | — | One URI instead of three flags: redis://user:pass@host:6379/1 (rediss:// for TLS) |
-3 | RESP2 | Speak RESP3 (same as sending HELLO 3 first) |
# Connect to the default host and portredis-cli
# Connect explicitly — useful when targeting a remote serverredis-cli -h 127.0.0.1 -p 6379
# Connect and immediately switch to database 1redis-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 0redis-cli -u redis://127.0.0.1:6379/1 DBSIZEInline mode vs interactive mode
Section titled “Inline mode vs interactive mode”In inline mode you pass a single command as arguments to redis-cli and the result is printed to stdout — handy for scripts:
redis-cli PINGIn 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 01) "v1" v12) (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 — verifying the connection
Section titled “PING — verifying the connection”PING is the lightest possible check that the server is reachable:
127.0.0.1:6379> PINGPONGPass an optional message and Redis echoes it back — useful for testing round-trip latency in scripts:
127.0.0.1:6379> PING "hello world""hello world"ECHO — sending a string back
Section titled “ECHO — sending a string back”ECHO returns the argument unchanged. It is mainly useful for testing that the client encodes strings correctly:
127.0.0.1:6379> ECHO "Hello""Hello"SELECT — switching databases
Section titled “SELECT — switching databases”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:
127.0.0.1:6379> SELECT 1OK127.0.0.1:6379[1]> SET greeting "hi from db1"OK127.0.0.1:6379[1]> SELECT 0OK127.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 — counting keys
Section titled “DBSIZE — counting keys”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:
127.0.0.1:6379> DBSIZE(integer) 0127.0.0.1:6379> SET a 1OK127.0.0.1:6379> SET b 2OK127.0.0.1:6379> DBSIZE(integer) 2FLUSHDB — clearing the current database
Section titled “FLUSHDB — clearing the current database”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:
127.0.0.1:6379> FLUSHDBOK127.0.0.1:6379> DBSIZE(integer) 0127.0.0.1:6379> FLUSHDB ASYNCOKFLUSHALL 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.
Built-in HELP
Section titled “Built-in HELP”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: stringThe 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 0Scripting flags you will actually use
Section titled “Scripting flags you will actually use”redis-cli -r 3 -i 0.2 INCR hits # repeat 3 times, 0.2 s apart -> 1 2 3echo -n "from stdin" | redis-cli -x SET note # -x: last argument read from stdinredis-cli -e LPUSH note q # -e: exit code 1 on an error replyredis-cli --scan --pattern 'user:*' # SCAN in a loop, never KEYS (next module)redis-cli --latency # live min/avg/max round-trip until Ctrl-Cprintf 'SET a 1\r\nINCR a\r\nLPUSH a x\r\n' | redis-cli --pipe # mass insertReal 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 valueAll 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.
RESP3 from the CLI
Section titled “RESP3 from the CLI”HELLO 3 switches the connection to RESP3 and returns what the server is:
127.0.0.1:6379> HELLO 31# "server" => "redis"2# "version" => "8.10.2"3# "proto" => (integer) 34# "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.
Reading AI-generated code
Section titled “Reading AI-generated code”An assistant writes a health-check script for a deploy pipeline:
🐛 Spot the bug
#!/bin/bash# fail the deploy if the session key is missingval=$(redis-cli GET session:42)if [ "$val" = "(nil)" ]; thenecho "no session" >&2exit 1fiecho "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:
#!/bin/bash# EXISTS returns 1 or 0 in raw mode; -e fails the script on server errorsn=$(redis-cli -e EXISTS session:42) || exit 2if [ "$n" -eq 0 ]; then echo "no session" >&2 exit 1fiecho "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.
Mental Model
Section titled “Mental Model”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.
Trade-offs
Section titled “Trade-offs”| Choice | Benefit | Cost |
|---|---|---|
Use redis-cli interactively | Fast to test commands, immediate feedback, great for debugging | No connection pooling, retries, or error handling that a real app needs |
| Use a client library in application code | Handles connection lifecycle, pipelining, and retries automatically | More code to write, an extra library dependency |
Switch databases with SELECT | Convenient for separating scratch data during dev | Not real multi-tenancy — every database still shares the same instance, memory, and performance |
Parse redis-cli output in shell scripts | No dependencies beyond the CLI | Output differs between terminal and pipe; error replies exit 0 unless you pass -e |
Common Misconceptions
Section titled “Common Misconceptions”- “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. - “
SELECTgives me separate Redis servers.” Same process, same memory, sameFLUSHALL. - “
-ais the safe way to pass a password.” It leaks into the process list; useREDISCLI_AUTHor-ufrom a secret store.
Common mistakes
Section titled “Common mistakes”- Connecting
redis-clidirectly to production to run fix-up commands — a typo can turn into aFLUSHDBorDELagainst real keys. Use a reviewed script or a client library instead when touching production data. - Assuming
SELECTis safe tenant isolation — the 16 logical databases still share CPU, memory, and a singleFLUSHALLaffects all of them at once. It is not a substitute for real per-tenant isolation. - Treating
redis-clias the only way application code talks to Redis — the healthy pattern isredis-clifor debugging/exploration only, with production code going through a client library that manages connection pooling and retries.