Tested on: Ubuntu 24.04 LTS, Redis 7.2.x (from the official
redisapt repository atpackages.redis.io). Configuration paths use/etc/redis/redis.conf— RHEL-family installs use/etc/redis/redis.confas well, so the file paths below are portable.
Why this matters
Almost every newsworthy Redis incident reduces to one of three failures:
- The server was bound to
0.0.0.0“for testing” and reachable from the internet. - There was no password (or the password was the example one from a tutorial).
FLUSHALL,CONFIG, orDEBUGwere left available, so a connected client could wipe data, exfiltrate keys, or write arbitrary files throughCONFIG SET dir/BGSAVE.
Redis is fast, in-memory, and trusts its clients by default. That trust is appropriate inside a private subnet with disciplined access controls. It is catastrophic everywhere else. This guide closes those three holes and adds the obvious next layer (TLS, resource limits, persistence safety).
1. Bind only where the clients actually live
Edit /etc/redis/redis.conf:
# Loopback only if Redis and the app share a host. Otherwise name
# the private interface explicitly — never '*' or 0.0.0.0.
bind 127.0.0.1 -::1
protected-mode yes
port 6379
protected-mode yes is a belt-and-braces fallback: if Redis is bound to a
non-loopback address and no authentication is configured, it refuses
remote connections outright. It is not a substitute for proper bind/auth
configuration — see the Gotchas section.
For a remote app server, bind to the private interface and add a host firewall rule that only permits the app server’s address:
bind 127.0.0.1 10.0.0.5
2. Use ACLs, not just requirepass
Redis 6+ supports per-user ACLs with command and key-pattern restrictions.
This is strictly better than the legacy single-password
(requirepass) approach, and you should use it for every application.
Create an ACL file at /etc/redis/users.acl (owned redis:redis, mode
600):
# Disable the default user — applications must authenticate explicitly.
user default off
# Application role: limited to its own key namespace, common commands only.
user appuser on >REPLACE_WITH_LONG_RANDOM_PASSWORD ~app:* &* +@read +@write +@hash +@list +@string +@set +@sortedset -@dangerous
# Read-only role for metrics/exporters.
user metrics on >REPLACE_WITH_ANOTHER_LONG_RANDOM_PASSWORD ~* &* +@read +ping +info +client|info -@dangerous
Reference the file from redis.conf:
aclfile /etc/redis/users.acl
Verify after restart:
1redis-cli -u "redis://appuser:[email protected]:6379" ACL WHOAMI
2redis-cli -u "redis://appuser:[email protected]:6379" ACL LIST
3. Rename or disable dangerous commands
The @dangerous category in step 2 already removes the worst offenders for
application roles. For a defence-in-depth layer that affects every user,
disable the commands no human or application should be running in
production:
# Disable outright — commands become unknown.
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command DEBUG ""
# Rename to an unguessable string if you need them for ops.
# Keep this string in your password manager, not in version control.
rename-command CONFIG "CONFIG_8f3a1e2c9b4d5"
rename-command SHUTDOWN "SHUTDOWN_8f3a1e2c9b4d5"
Renames require a Redis restart to take effect and apply globally — they override ACLs.
4. TLS in transit (Redis 6+)
Redis supports TLS natively. There is no excuse for cleartext Redis traffic crossing any network boundary, including private VLANs.
# In redis.conf:
port 0 # Disable the cleartext port.
tls-port 6379
tls-cert-file /etc/redis/tls/redis.crt
tls-key-file /etc/redis/tls/redis.key
tls-ca-cert-file /etc/redis/tls/ca.crt
tls-auth-clients no # Or 'yes' to require client certs (mTLS).
tls-protocols "TLSv1.2 TLSv1.3"
File ownership: certificates owned redis:redis, key with mode 600.
Client connection string becomes rediss:// (note the double s).
5. Set resource limits
A Redis instance with no maxmemory will happily consume every byte of
RAM and the OS OOM killer will choose its victims arbitrarily.
# Bound memory and pick an eviction policy. For caches, allkeys-lru is
# usually right. For session stores, choose noeviction and let writes
# fail rather than silently lose sessions.
maxmemory 2gb
maxmemory-policy allkeys-lru
# Refuse idle and slow clients to limit DoS surface.
timeout 300
tcp-keepalive 60
tcp-backlog 511
# Cap the slow-query log so it doesn't itself become a memory leak.
slowlog-log-slower-than 10000 # microseconds
slowlog-max-len 128
6. Persistence: do not leak through the dump file
If you use AOF or RDB persistence, the dump files contain every key. Two common mistakes:
- Default directory is
/var/lib/redis, ownedredis:rediswith mode750— verify, don’t assume. - Backups of these files are backups of personal data. Encrypt the backup destination.
Recommended:
dir /var/lib/redis
dbfilename dump.rdb
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
7. Apply and verify
1# Test config syntax before reloading — Redis ships no built-in
2# 'config check', so a quick redis-cli loop is the substitute.
3sudo systemctl restart redis-server
4sudo systemctl status redis-server
5
6# Confirm the listening posture.
7sudo ss -ltnp | grep redis
8
9# Confirm authentication is mandatory.
10redis-cli ping # should return NOAUTH if ACL/default-off worked
11redis-cli -u "rediss://appuser:[email protected]:6379" ping # should return PONG
Gotchas
protected-mode is not authentication
protected-mode yes blocks remote connections only when no auth is
configured. The moment you set requirepass or an ACL with a password,
protected-mode no longer blocks anything. The protection it gives is
“refuse to expose an unauthenticated Redis to the network” — not “block
unauthorised users”.
Command renames apply globally and cannot be ACL-scoped
rename-command CONFIG "" disables CONFIG for every user, including
internal ones. If a redis-exporter or sentinel needs CONFIG GET, you’ll
break it. Audit your operational tooling before disabling commands wholesale.
CONFIG SET plus BGSAVE is a file-write primitive
An attacker with CONFIG access can change dir and dbfilename and
trigger BGSAVE to write a Redis dump anywhere on disk Redis can write —
including SSH authorized_keys files if Redis runs with too much
filesystem access. Renaming or disabling CONFIG removes this primitive
entirely; least-privilege filesystem permissions are a complementary
defence.
Replication needs its own auth and TLS
If you set up replicas, masterauth and masteruser must be configured,
and TLS settings duplicated for replication traffic. The same hardening
applies — replica-read-only yes is the default but worth re-verifying
after any config change.
Companion script
A copy-paste audit script that reports on bind, auth, command renames, TLS
posture, and resource limits is planned at
/scripts/redis-check/.
What this guide deliberately does not cover
- Redis Sentinel / Cluster topology hardening — separate guide.
- Redis as a session store specifically — covered as part of
app-layer-encryption(planned). - redis-exporter / Prometheus integration — separate guide.