Redis Security Basics (requirepass, ACLs)
By default, a freshly installed Redis server trusts everyone who can reach its port: no password, no login, and full permission to run every command including CONFIG and FLUSHALL. Redis was designed to sit behind trusted application code on a private network, not to be exposed directly to the internet, so authentication is opt-in rather than mandatory. Two mechanisms let you lock a server down: the legacy requirepass password, and the far more powerful Access Control List (ACL) system introduced in Redis 6. This lesson covers both, how they relate to each other, and how to design a least-privilege permission model for a real application.
Overview: How Redis Authentication and Authorization Work
Every connection to Redis is associated with a user. Even if you never configure anything, every client is implicitly authenticated as the built-in default user, which out of the box is: enabled (on), has no password (nopass), can run every command (+@all), can touch every key (~*), and can publish/subscribe to every channel (&*). That is why a fresh redis-cli session just works with no login step.
requirepass is the old, single-password model: it sets one password on the default user. It is all-or-nothing — anyone who knows the password gets the same full +@all ~* &* access as before, just gated behind a login. Under the hood, modern Redis actually implements requirepass as sugar over the ACL system: setting it is equivalent to running ACL SETUSER default >password, and clearing it is equivalent to giving default the nopass rule again.
The ACL system (Redis 6+) replaces “one shared password” with named users, each carrying its own independent rule set:
- State —
onoroff(a disabled user cannot authenticate at all, even with a correct password). - Passwords — zero or more SHA-256 hashes (added with
>password), or the specialnopassrule. - Commands — allowed with
+commandor+@category(e.g.+@read), denied with-commandor-@category. A newly created user starts with no commands allowed at all (effectively-@all) until you explicitly grant some. - Key patterns — glob patterns granted with
~pattern(e.g.~app:*); a user with no key patterns can authenticate but cannot read or write a single key. - Pub/Sub channels — glob patterns granted with
&pattern, independent of key access.
Because Redis executes commands one at a time on a single thread, the authentication check and every permission check happen synchronously, immediately before the command runs — there is no window where a command could slip through between a permission change and its enforcement.
Persistence is a common trap: setting requirepass or creating ACL users at runtime only changes the running server’s memory. To survive a restart, the password must also live in redis.conf (or you must persist the change with CONFIG REWRITE), and ACL users must be saved to an ACL file (configured via aclfile in redis.conf) with ACL SAVE, or defined directly as user lines in that file. Also remember that authentication and authorization are not encryption — requirepass and ACLs stop an unauthorized client from running commands, but they do nothing to encrypt traffic on the wire; that is a separate concern handled by enabling TLS.
Syntax
The general forms you will use most often:
ACL SETUSER <username> <rule> [<rule> ...]
ACL GETUSER <username>
ACL DELUSER <username>
ACL LIST
ACL WHOAMI
AUTH <username> <password>
CONFIG SET requirepass <password>
| Rule token | Meaning |
|---|---|
on / off |
Enable or disable the user (disabled users can never authenticate) |
>password |
Add a password (Redis stores only its SHA-256 hash) |
nopass |
Allow authenticating with any password, or none |
~pattern |
Grant access to keys matching a glob pattern (~* = all keys) |
&pattern |
Grant access to pub/sub channels matching a glob pattern |
+command / -command |
Allow or deny one specific command |
+@category / -@category |
Allow or deny a whole command category, e.g. +@read, -@dangerous |
resetkeys / resetchannels |
Remove all previously granted key or channel patterns |
Examples
Example 1: Inspecting the default user
ACL WHOAMI
ACL LIST
Output:
"default"
1) "user default on nopass ~* &* +@all"
ACL WHOAMI confirms which user the current connection is authenticated as, and ACL LIST prints every configured user as a rule string in the same syntax used to create them. On an unconfigured server there is exactly one user, default, with nopass, every key (~*), every channel (&*), and every command (+@all) — the wide-open state described above.
Example 2: Creating a least-privilege application user
ACL SETUSER appuser on >mypassword123 ~app:* +get +set
ACL WHOAMI
AUTH appuser mypassword123
SET app:session:42 "active"
GET app:session:42
Output:
OK
"default"
OK
OK
"active"
This creates a new user named appuser that is enabled, has one password, can only touch keys under the app: namespace, and can only run GET and SET — nothing else, not even DEL or KEYS. The connection starts out as default, and after a successful AUTH it switches identity to appuser for the rest of that connection. The final SET/GET pair succeeds because app:session:42 matches the app:* pattern and both commands are explicitly permitted.
Example 3: Inspecting and removing a user
ACL SETUSER reportuser on >readpass456 ~report:* +get +mget +exists
ACL GETUSER reportuser
ACL CAT
ACL DELUSER reportuser
Output:
OK
1) "flags"
2) 1) "on"
3) "passwords"
4) 1) "d6b1c1c3e6f0b6a2b7e0e5b1a2f3c4d5e6f7089abcdef1234567890abcdef12"
5) "commands"
6) "-@all +get +mget +exists"
7) "keys"
8) "~report:*"
9) "channels"
10) ""
11) "selectors"
12) (empty array)
1) "keyspace"
2) "read"
3) "write"
4) "set"
5) "sortedset"
6) "list"
7) "hash"
8) "string"
9) "bitmap"
10) "hyperloglog"
11) "geo"
12) "stream"
13) "pubsub"
14) "admin"
15) "fast"
16) "slow"
17) "blocking"
18) "dangerous"
19) "connection"
20) "transaction"
21) "scripting"
(integer) 1
ACL GETUSER returns the full rule set for one user as a flat field/value array — useful for auditing exactly what a user can do. ACL CAT lists every command category available for use with +@category/-@category rules (the real list is longer than shown here). ACL DELUSER removes the user and returns the count of users actually deleted; any connection still authenticated as that user is immediately dropped to the default permission set on its next command.
How It Works Step by Step
When a client sends a command, Redis performs this sequence on its single command-processing thread before doing any work:
- Resolve which user the connection is currently authenticated as (
defaultuntil anAUTHsucceeds, unlessdefaultitself isnopass). - If the user is
off, reject every command outright. - Check whether the command (or its category) is in the user’s allow list; if not, reply with a
NOPERMerror and never touch the keyspace. - For commands that take key arguments, check every key against the user’s granted key patterns; any key that does not match any pattern also triggers
NOPERM. - Only after every check passes does Redis actually execute the command against the data structures.
An AUTH call itself is checked by hashing the supplied password with SHA-256 and comparing it against the stored hashes for that user; a mismatch (or a user with zero passwords and no nopass rule) returns a WRONGPASS error, not a silent failure.
Common Mistakes
Mistake 1 — forgetting that runtime changes don’t survive a restart. Running CONFIG SET requirepass mypassword protects the server immediately, but on the next restart Redis reloads only redis.conf, and the password is gone unless you also added requirepass mypassword to the config file or ran CONFIG REWRITE to persist the in-memory config back to disk. The fix: always set security-relevant config in redis.conf (or an included file) as the source of truth, and treat CONFIG SET as a way to change it live, not a substitute for the file.
Mistake 2 — creating a user without granting any key pattern, then wondering why every command fails.
ACL SETUSER limiteduser on >pass123 +get
AUTH limiteduser pass123
GET report:total
Output:
OK
OK
(error) NOPERM No permissions to access a key
limiteduser was granted the GET command but zero key patterns, so it can authenticate but cannot read a single key. The fix is to always pair command grants with a key pattern, e.g. ~report:*, even if it’s just ~* during early development.
Mistake 3 — enabling a user without giving it any way to authenticate.
ACL SETUSER brokenuser on ~app:* +get
AUTH brokenuser anypassword
Output:
OK
(error) WRONGPASS invalid username-password pair or user is disabled.
brokenuser is on but has neither a password (>password) nor nopass, so no credential can ever satisfy AUTH for it — the user is effectively unusable until you add one or the other.
Best Practices
- Never run Redis with no password on a network-reachable interface; at minimum set
requirepass, and prefer ACL users over sharing one password across every client. - Give each application (or each service) its own ACL user scoped with the narrowest
~patternand command set it actually needs — a checkout service should not be able to runFLUSHALLor read a completely different service’s key namespace. - Deny dangerous administrative commands (
-@admin,-@dangerous, or explicit-flushall -flushdb -config -shutdown) for every user except a dedicated ops/admin user. - Persist ACL users to an
aclfileand runACL SAVEafter changes, so a restart doesn’t silently revert your permission model to whatever is left inredis.conf. - Rotate passwords by adding a new password with
>newpasswordbefore removing the old one with<oldpassword, so in-flight clients aren’t locked out mid-rotation. - Combine ACLs with network-level controls (binding to a private interface, a firewall, or
protected-mode) and with TLS for encryption in transit — ACLs handle who can do what, not who can listen on the wire. - Audit periodically with
ACL LISTandACL GETUSERto catch overly broad grants (like a forgotten~*) before they become an incident.
Practice Exercises
- Create an ACL user named
analyticsthat can only run read-only commands (GET,MGET,EXISTS) against keys under themetrics:*namespace, and verify withAUTHthat it can read ametrics:*key but getsNOPERMon a key outside that namespace. - Using
ACL GETUSER, audit theappusercreated in Example 2 and identify exactly which commands and key patterns it holds; then useACL SETUSERto also deny it the ability to runSET, leaving onlyGET. - Design (on paper) an ACL layout for a small app with three services — a web API, a background worker, and a metrics dashboard — each needing different keys and commands; write out the
ACL SETUSERcommand you’d run for each one.
Summary
requirepasssets one password on the built-indefaultuser and grants all-or-nothing access — simple, but not fine-grained.- The ACL system (Redis 6+) defines named users, each with its own enabled state, password(s), allowed commands/categories, key patterns, and channel patterns.
- A brand-new ACL user starts with no command access at all; you must explicitly grant commands and key patterns, or the user is locked out even after a successful
AUTH. - Permission checks happen synchronously on Redis’s single command thread before any keyspace access, so there is no race between an ACL change and its enforcement.
- Runtime changes via
CONFIG SETorACL SETUSERdon’t survive a restart unless persisted toredis.conf/CONFIG REWRITEor anaclfile/ACL SAVE. - ACLs control authentication and authorization only — pair them with network controls and TLS for a complete security posture.
| Command | Time Complexity |
|---|---|
AUTH |
O(N) where N is the number of passwords configured for the user |
ACL WHOAMI |
O(1) |
ACL LIST |
O(N) where N is the number of configured users |
ACL CAT |
O(1), or O(N) when listing commands within one category |
ACL GETUSER |
O(N) where N is the number of rules the user has |
ACL SETUSER |
O(N) where N is the number of rules supplied |
ACL DELUSER |
O(1) amortized per user removed |
CONFIG SET requirepass |
O(1) |
