Recipe: Redis
Persistence, memory limits, and auth.
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.
# a minimal hardened config, mounted read-only into the containerrequirepass ${REDIS_PASSWORD} # auth onmaxmemory 256mb # do not eat the hostmaxmemory-policy allkeys-lru # evict, do not OOMappendonly yes # durable persistence (AOF)save "" # (disable RDB snapshots if AOF is enough)
services:redis:image: redis:7-alpinecommand: ["redis-server", "/etc/redis/redis.conf"]environment:REDIS_PASSWORD_FILE: /run/secrets/redis_pw # if using an entrypoint that reads itvolumes:- ./redis.conf:/etc/redis/redis.conf:ro- redisdata:/datahealthcheck:test: ["CMD", "redis-cli", "ping"]interval: 10s# no ports: — only reachable from other services on the networkvolumes: { 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.
$ docker compose up -d$ docker compose exec redis redis-cli -a "$(cat secrets/redis_pw.txt)" pingPONG$ docker compose exec app sh -c 'nc -z redis 6379 && echo reachable-internally'reachable-internally # from inside the stack, by name, no published port