CoursesDocker in depthPractical recipes

Recipe: Redis

Persistence, memory limits, and auth.

Intermediate10 min · lesson 23 of 30

Redis is trivial to start and trivial to misconfigure: run redis with no options and you get an in-memory cache with no password, no persistence, and no memory ceiling — an open data store that will happily consume all the host’s RAM. The secure recipe adds four things: a password, a persistence choice, a maxmemory policy, and a bind that does not expose it to the world.

redis.conf
# a minimal hardened config, mounted read-only into the container
requirepass ${REDIS_PASSWORD} # auth on
maxmemory 256mb # do not eat the host
maxmemory-policy allkeys-lru # evict, do not OOM
appendonly yes # durable persistence (AOF)
save "" # (disable RDB snapshots if AOF is enough)
compose.yaml
services:
redis:
image: redis:7-alpine
command: ["redis-server", "/etc/redis/redis.conf"]
environment:
REDIS_PASSWORD_FILE: /run/secrets/redis_pw # if using an entrypoint that reads it
volumes:
- ./redis.conf:/etc/redis/redis.conf:ro
- redisdata:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
# no ports: — only reachable from other services on the network
volumes: { redisdata: {} }

Persistence: RDB vs AOF

Redis offers two durability modes. RDB takes periodic point-in-time snapshots — compact and fast to restore, but you can lose the writes since the last snapshot. AOF (append-only file) logs every write, so you lose at most a second or two, at the cost of a larger file. For a cache you may want neither; for anything you would miss, mount /data on a volume and enable AOF, as above. The volume is what makes either survive a restart.

Do not publish it

The most common Redis incident is an instance published to 0.0.0.0 with no password and found by an internet scan within hours. Inside a Compose stack, other services reach redis by name with no published port at all — so omit ports entirely. If something outside the stack genuinely must connect, bind to 127.0.0.1 and require a password, never a bare -p 6379:6379.

terminal
$ docker compose up -d
$ docker compose exec redis redis-cli -a "$(cat secrets/redis_pw.txt)" ping
PONG
$ docker compose exec app sh -c 'nc -z redis 6379 && echo reachable-internally'
reachable-internally # from inside the stack, by name, no published port
No password + published port = data breach
An unauthenticated Redis on a reachable port is one of the most-exploited misconfigurations on the internet — attackers use it to read your data and, on some setups, write cron jobs to the host. Always set requirepass, cap maxmemory so it cannot take down the node, and keep it off published ports unless there is no alternative.