Valkey’s Access Control List (ACL) doesn’t just grant or deny access; it’s a sophisticated security model that allows you to sculpt granular permissions down to individual commands for specific users.

Let’s watch Valkey ACLs in action. Imagine a valkey-cli session where we’re trying to perform an operation we shouldn’t be able to.

# First, let's connect as a user with limited privileges
valkey-cli -u valkey://user_limited:password_limited

# Now, try to execute a command that requires higher privileges, like CONFIG GET
127.0.0.1:6379> CONFIG GET dir
(error) ERR. unknown command `CONFIG`

That ERR. unknown command 'CONFIG' is our signal. The user_limited account, as defined in our ACL, simply doesn’t have permission to run the CONFIG command.

The Problem Valkey ACL Solves

At its core, Valkey ACLs address the need for robust security in multi-tenant environments or when exposing Valkey to untrusted networks. Instead of a single, all-powerful master password, you can create distinct user identities, each with a precisely defined set of capabilities. This prevents accidental data corruption, unauthorized access to sensitive configurations, and limits the blast radius if one user’s credentials are compromised.

How Valkey ACLs Work Internally

Valkey stores ACL rules in a special internal format. When a client connects, Valkey authenticates them and then applies the corresponding ACL rules to every command they attempt to execute. These rules are evaluated against a set of predefined categories and individual commands. The system checks if the authenticated user has been granted permission for the specific command category or the exact command. If not, it returns an error.

The ACL system is managed through the ACL SETUSER command. This is where you define users, their passwords, and their permissions.

Let’s set up a more complex scenario. We’ll create a read-only user and a user that can manage a specific set of keys.

First, ensure you have a Valkey instance running with ACLs enabled. This is usually the default in modern versions.

# Connect as the default root user (or a user with ACL privileges)
valkey-cli

# Create a read-only user
127.0.0.1:6379> ACL SETUSER readonly on >readonly_password ~* +@read +ping +hello
OK

# Create a user that can only modify keys prefixed with "app:"
127.0.0.1:6379> ACL SETUSER app_manager on >app_password ~app:* +@write -@dangerous +select +scan
OK

# Verify the users
127.0.0.1:6379> ACL LIST
1) 1) "readonly"
   2) 1) "on"
      2) "password"
      3) "readonly_password"
   3) 1) "flags"
      2) "on"
   4) 1) "rules"
      2) "~* +@read +ping +hello"
2) 1) "app_manager"
   2) 1) "on"
      2) "password"
      3) "app_password"
   3) 1) "flags"
      2) "on"
   4) 1) "rules"
      2) "~app:* +@write -@dangerous +select +scan"

Now, let’s test these users.

Connect as readonly:

valkey-cli -u valkey://readonly:readonly_password
127.0.0.1:6379> SET mykey somevalue
(error) NOAUTH Anonymous User
# Note: This error might appear if the user is not explicitly configured to allow SET.
# The '@read' category typically includes commands like GET, MGET, SCAN, etc.

127.0.0.1:6379> GET mykey
(nil) # Assuming mykey doesn't exist yet

127.0.0.1:6379> SET anotherkey somevalue
(error) NOAUTH Anonymous User # This is expected for SET if only @read is granted.

127.0.0.1:6379> INFO memory
# Memory
used_memory:123456
... # This works because INFO is part of the @read category.

Connect as app_manager:

valkey-cli -u valkey://app_manager:app_password
127.0.0.1:6379> SET app:mykey somevalue
OK

127.0.0.1:6379> GET app:mykey
"somevalue"

127.0.0.1:6379> SET other:mykey somevalue
(error) NOAUTH Anonymous User # Cannot modify keys not prefixed with "app:"

127.0.0.1:6379> SHUTDOWN
(error) NOAUTH Anonymous User # SHUTDOWN is a dangerous command and blocked by -@dangerous.

The Levers You Control

The ACL SETUSER command is your primary tool. It takes the following key arguments:

  • username: The name of the user.
  • on|off: Enables or disables the user account.
  • password: The user’s password, prefixed with > for hashed passwords.
  • ~ <key_pattern>: Restricts access to keys matching the pattern. ~* means all keys. ~app:* means keys starting with app:.
  • +<command>: Grants permission for a specific command.
  • -<command>: Denies permission for a specific command.
  • +@<category>: Grants permission for all commands in a predefined category (e.g., @read, @write, @set).
  • -@<category>: Denies permission for all commands in a category.
  • NOCAPS: Disables all default capabilities, forcing you to explicitly grant every permission.
  • ALL: Grants all capabilities by default, which you can then restrict.

The One Thing Most People Don’t Know

Many users, when setting up read-only access, simply grant the @read category. However, this category includes commands like SCAN and KEYS which, while read operations, can be incredibly resource-intensive on large datasets and potentially disrupt performance. For true, safe read-only access, you often need to explicitly list the desired read commands (GET, MGET, HGET, LRANGE, etc.) and potentially exclude KEYS and SCAN if your dataset is large, or at least restrict their MATCH pattern.

Next Steps

Once you’ve mastered user and key-specific permissions, you’ll want to explore the nuances of command categories and the powerful ACL GENPASS command for secure password generation.

Want structured learning?

Take the full Valkey course →