How to Run Redis 8 with Docker and Docker Compose

Run Redis 8 with Docker Compose: pinned image, password from .env, redis.conf, AOF and RDB persistence, healthcheck, bundled modules and tested backups.

Running Redis 8 with Docker Compose gives you a cache, queue or key-value store for local development, CI, or a single-server deployment without installing Redis on the host. In this guide you will run the official redis:8.10.3 image with a pinned tag, a password from an .env file, a mounted redis.conf, AOF and RDB persistence on a named volume, and a healthcheck. You will then back Redis up and restore it, including the restore mistake that silently gives you an empty database.

Everything below was tested on Docker Engine 29 with Redis 8.10.3.

What changed in Redis 8

  • Modules are bundled. The official image ships Redis Query Engine (search), JSON, time series and probabilistic data structures (bf), plus vector sets. The entrypoint loads them automatically, as long as the container command starts with redis-server (see Step 3).
  • Licensing. Redis 8 is available under AGPLv3 again, alongside RSALv2 and SSPLv1. If that matters for your use, read the license, or look at Valkey, the BSD-licensed fork.
  • Config, redis-cli and the protocol are the same as Redis 6 and 7, so existing clients keep working.

Prerequisites

docker version --format '{{.Server.Version}}'
docker compose version

Quick start with docker run

For a throwaway instance:

docker run -d --name redis-test -p 127.0.0.1:6379:6379 redis:8.10.3
docker exec -it redis-test redis-cli ping
PONG

This has no password and no persistence settings. Remove it with docker rm -f redis-test. For anything you keep, use Compose.

Step 1: Create the project directory

mkdir -p ~/redis && cd ~/redis

Step 2: Store the password in an .env file

Create .env:

REDIS_PASSWORD=change-me-to-a-long-random-password

Generate a strong one with openssl rand -base64 32, then lock the file down and keep it out of Git:

chmod 600 .env
echo ".env" >> .gitignore

Compose reads .env in two ways in this setup: to fill in ${REDIS_PASSWORD} in compose.yaml, and (through env_file) as an environment variable inside the container for the healthcheck.

Step 3: Write redis.conf

Create redis.conf:

# Network: listen inside the container; the host port mapping controls exposure
bind 0.0.0.0
protected-mode yes
port 6379

# Memory: cap usage and evict least-recently-used keys with a TTL first
maxmemory 512mb
maxmemory-policy volatile-lru

# Persistence: RDB snapshots plus an append-only file
save 3600 1 300 100 60 10000
appendonly yes
appendfsync everysec
dir /data

What these do:

  • maxmemory and maxmemory-policy: without a cap, Redis grows until the kernel’s OOM killer stops it. volatile-lru evicts keys that have a TTL. For a pure cache where every key may go, use allkeys-lru. For a queue or primary store, use noeviction so writes fail instead of data disappearing.
  • appendonly yes with appendfsync everysec: every write is logged, so you lose at most about one second of data on a crash.
  • save: periodic RDB snapshots (dump.rdb), which are compact and handy for backups.

Keep the password out of redis.conf. The file is bind-mounted and has to be readable by the redis user inside the container, so it is not the place for a secret.

Step 4: Create the Compose file

Create compose.yaml:

services:
  redis:
    image: redis:8.10.3
    container_name: redis
    restart: unless-stopped
    env_file: .env
    command:
      - redis-server
      - /usr/local/etc/redis/redis.conf
      - --requirepass
      - ${REDIS_PASSWORD:?set REDIS_PASSWORD in .env}
    ports:
      - "127.0.0.1:6379:6379"
    volumes:
      - redis_data:/data
      - ./redis.conf:/usr/local/etc/redis/redis.conf:ro
    healthcheck:
      test: ["CMD-SHELL", "REDISCLI_AUTH=\"$$REDIS_PASSWORD\" redis-cli ping | grep -q PONG"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s

volumes:
  redis_data:

Key points:

  • The command starts with redis-server. The image’s entrypoint only drops root privileges and loads the bundled modules when it sees redis-server as the first word. A common pattern, command: sh -c "redis-server ... --requirepass $$REDIS_PASSWORD", works but runs Redis as root and without modules. I tested both: with sh -c, /proc/1/status showed Uid: 0 and MODULE LIST returned only vectorset.

  • ${REDIS_PASSWORD:?...}: Compose refuses to start if the variable is empty, instead of starting Redis with no password:

    error while interpolating services.redis.command.[]: required variable REDIS_PASSWORD is missing a value: set REDIS_PASSWORD in .env
    
  • REDISCLI_AUTH lets redis-cli authenticate without putting the password on its command line. $$ stops Compose from expanding it, so the container’s environment is used.

  • 127.0.0.1:6379:6379: reachable from the host only. Other containers in the same project connect to redis:6379 without any port mapping. Never publish Redis to the internet; exposed instances are routinely scanned and attacked.

  • Pinned tag: bump 8.10.3 on purpose after reading the release notes, not by pulling latest.

Step 5: Start Redis

docker compose up -d --wait
 Container redis Healthy

Check the version, the settings and the loaded modules:

docker exec -e REDISCLI_AUTH="$(grep ^REDIS_PASSWORD .env | cut -d= -f2-)" redis \
  redis-cli INFO server | grep redis_version
redis_version:8.10.3

To save typing, open a shell into redis-cli and authenticate once:

docker exec -it redis redis-cli
127.0.0.1:6379> AUTH change-me-to-a-long-random-password
OK
127.0.0.1:6379> CONFIG GET maxmemory-policy
1) "maxmemory-policy"
2) "volatile-lru"
127.0.0.1:6379> MODULE LIST

MODULE LIST should show search, vectorset, bf, timeseries and ReJSON.

Without the password, commands are rejected:

docker exec redis redis-cli ping
NOAUTH Authentication required.

Confirm Redis is not running as root:

docker exec redis grep Uid /proc/1/status
Uid:	999	999	999	999

Step 6: Store and read data

Inside redis-cli (after AUTH):

127.0.0.1:6379> SET greeting "hello from compose"
OK
127.0.0.1:6379> GET greeting
"hello from compose"
127.0.0.1:6379> JSON.SET doc $ '{"a":1}'
OK
127.0.0.1:6379> JSON.GET doc
"{\"a\":1}"

Applications connect with a URL such as:

redis://:[email protected]:6379/0

Step 7: Check persistence

Recreate the container and confirm the keys survived:

docker compose down
docker compose up -d --wait
docker exec -e REDISCLI_AUTH="$(grep ^REDIS_PASSWORD .env | cut -d= -f2-)" redis redis-cli GET greeting
hello from compose

The volume now holds the RDB snapshot and the multi-part AOF:

docker exec redis ls /data /data/appendonlydir
/data:
appendonlydir
dump.rdb

/data/appendonlydir:
appendonly.aof.1.base.rdb
appendonly.aof.1.incr.aof
appendonly.aof.manifest

docker compose down keeps the volume. docker compose down -v deletes it, with all your data.

Step 8: Back up and restore Redis

Option 1: back up the whole volume

This keeps both the AOF and the RDB and restores exactly. Compose prefixes the volume with the project (directory) name, so check it first:

docker volume ls | grep redis_data

For a project directory named redis, the volume is redis_redis_data:

docker compose stop redis
docker run --rm -v redis_redis_data:/data -v "$PWD":/backup alpine \
  tar czf /backup/redis-data-$(date +%F).tar.gz -C /data .
docker compose start redis

Restore:

docker compose stop redis
docker run --rm -v redis_redis_data:/data -v "$PWD":/backup alpine \
  sh -c 'rm -rf /data/* && tar xzf /backup/redis-data-2026-11-10.tar.gz -C /data'
docker compose start redis

Option 2: copy an RDB snapshot while Redis runs

Trigger a snapshot and copy it out, with no downtime:

export REDISCLI_AUTH="$(grep ^REDIS_PASSWORD .env | cut -d= -f2-)"
docker exec -e REDISCLI_AUTH redis redis-cli BGSAVE
docker exec -e REDISCLI_AUTH redis redis-cli INFO persistence | grep rdb_last_bgsave_status
docker cp redis:/data/dump.rdb ./dump-$(date +%F).rdb

Wait for rdb_last_bgsave_status:ok before copying.

Restoring an RDB file needs one extra step when AOF is on. If you copy dump.rdb into an empty volume and start Redis with appendonly yes, Redis creates a new, empty AOF and loads that instead. You get an empty database, with no error. I tested this: DBSIZE returned 0.

Start Redis once with AOF off so it loads the RDB, then turn AOF on, which writes a new AOF from the loaded data:

docker compose stop redis
docker run --rm -v redis_redis_data:/data -v "$PWD":/backup alpine \
  sh -c 'rm -rf /data/* && cp /backup/dump-2026-11-10.rdb /data/dump.rdb'

# Temporary instance: no published ports, AOF off
docker compose run -d --rm --name redis-restore redis \
  redis-server /usr/local/etc/redis/redis.conf --appendonly no
docker exec redis-restore redis-cli DBSIZE
docker exec redis-restore redis-cli CONFIG SET appendonly yes
docker stop redis-restore

docker compose up -d --wait

The temporary instance has no password, but docker compose run publishes no ports and protected mode only allows loopback connections, so only docker exec can reach it.

Step 9: Tune the host

Redis logs a warning at startup if memory overcommit is disabled on the Docker host, because background saves (BGSAVE, AOF rewrites) can then fail under memory pressure. On a Linux host, enable it:

echo 'vm.overcommit_memory = 1' | sudo tee /etc/sysctl.d/99-redis.conf
sudo sysctl --system

This is a host kernel setting and cannot be set from inside the container.

Useful commands

docker compose logs -f redis                                       # follow the server log
export REDISCLI_AUTH="$(grep ^REDIS_PASSWORD .env | cut -d= -f2-)"
docker exec -e REDISCLI_AUTH redis redis-cli INFO memory | grep used_memory_human
docker exec -e REDISCLI_AUTH redis redis-cli INFO keyspace         # keys per database
docker exec -e REDISCLI_AUTH redis redis-cli --latency             # live latency (Ctrl+C to stop)
docker exec -e REDISCLI_AUTH redis redis-cli --bigkeys             # find the largest keys

Conclusion

You now have Redis 8 running with Docker Compose: a pinned image, the password kept out of the config file and enforced at startup, a non-root process with the bundled modules loaded, AOF and RDB persistence on a named volume, a healthcheck, and two tested backup and restore methods. For high availability, add replicas with Redis Sentinel, or use Redis Cluster for sharding.

comments powered by Disqus
Citizix Ltd
Built with Hugo
Theme Stack designed by Jimmy