Tested on: Ubuntu 24.04 LTS, Redis 7.2.x (from the official redis apt repository at packages.redis.io). Configuration paths use /etc/redis/redis.conf — RHEL-family installs use /etc/redis/redis.conf as well, so the file paths below are portable.

Why this matters

Almost every newsworthy Redis incident reduces to one of three failures:

  1. The server was bound to 0.0.0.0 “for testing” and reachable from the internet.
  2. There was no password (or the password was the example one from a tutorial).
  3. FLUSHALL, CONFIG, or DEBUG were left available, so a connected client could wipe data, exfiltrate keys, or write arbitrary files through CONFIG 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, owned redis:redis with mode 750 — 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.