Redis: Use Docker Compose for Redis

This is how to create a Redis single node or a Redis cluster using Docker Compose

Redis: Use Docker Compose for Redis

Redis is an in-memory NoSQL database for key value store. It claims to be "the world’s fastest data platform for caching, vector search, and NoSQL DBs".

Creating a Docker container for Redis, you don't need much in your Docker compose file:

services:
  db1:
    image: redis

We can pull the image and start the container:

# docker compose pull
[+] Pulling 14/14
 ✔ db1 Pulled                            3.0s
   ✔ 4d2547c08499 Already exists         0.0s
   ✔ 13dec22004c9 Pull complete          0.5s
   ✔ 00a9faca8e4e Pull complete          0.5s
   ✔ 658b37ee7a86 Pull complete          0.6s
   ✔ 35d5968306fa Pull complete          1.3s
   ✔ 2dd354c361c9 Pull complete          1.3s
   ✔ 4f4fb700ef54 Pull complete          1.3s
   ✔ 1eecbca6a5db Pull complete         
[+] Running 1/1
 ✔ Container redis-db1-1  Started        0.9s
# docker compose up -d
1:C 18 Feb 2025 10:40:21.896 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
1:C 18 Feb 2025 10:40:21.904 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 18 Feb 2025 10:40:21.904 * Redis version=7.4.2, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 18 Feb 2025 10:40:21.904 * Configuration loaded
1:M 18 Feb 2025 10:40:21.905 * monotonic clock: POSIX clock_gettime
1:M 18 Feb 2025 10:40:21.913 * Running mode=standalone, port=6379.
1:M 18 Feb 2025 10:40:21.914 * No cluster configuration found, I'm 2ac80692b2ccccc1b0f25ca700aaa4265aa2688f
1:M 18 Feb 2025 10:40:21.922 * Server initialized
1:M 18 Feb 2025 10:40:21.926 * Creating AOF base file appendonly.aof.1.base.rdb on server start
1:M 18 Feb 2025 10:40:21.936 * Creating AOF incr file appendonly.aof.1.incr.aof on server start
1:M 18 Feb 2025 10:40:21.937 * Ready to accept connections tcp

But Redis has a great cluster functionality. Let's start not one, but 6 containers :-) Here's my docker-compose.yml:

services:
  db1:
    image: redis
    hostname: redis1
    networks:
      - redis
    command: redis-server --bind 0.0.0.0 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 3000 --appendonly yes --appendfilename appendonly.aof

  db2:
    image: redis
    hostname: redis2
    networks:
      - redis
    command: redis-server --bind 0.0.0.0 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 3000 --appendonly yes --appendfilename appendonly.aof

  db3:
    image: redis
    hostname: redis3
    networks:
      - redis
    command: redis-server --bind 0.0.0.0 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 3000 --appendonly yes --appendfilename appendonly.aof

  db4:
    image: redis
    hostname: redis4
    networks:
      - redis
    command: redis-server --bind 0.0.0.0 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 3000 --appendonly yes --appendfilename appendonly.aof

  db5:
    image: redis
    hostname: redis5
    networks:
      - redis
    command: redis-server --bind 0.0.0.0 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 3000 --appendonly yes --appendfilename appendonly.aof

  db6:
    image: redis
    hostname: redis6
    networks:
      - redis
    command: redis-server --bind 0.0.0.0 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 3000 --appendonly yes --appendfilename appendonly.aof

networks:
  redis:
    name: redis

Ok, this is quite a lot. Let's have a look:

  • hostname is needed to be able to access the servers/containers
  • network: we use the network redis to make all the Redis servers ready to communicate
  • command: by default, Redis starts as standalone service. We explicitly tell the container how to start:
    • bind: 0.0.0.0 means that the service listens on all interfaces
    • cluster-enabled to enable cluster functionality
    • cluster-config-file is the name of the file
    • additionally, some parameters I don't have to explain:
      • cluster-node-timeout
      • apppendonly
      • appendfilename

After starting all the containers using

# docker compose pull
# docker compose up -d

we can see that they are running. But the 6 services are not yet configured as a cluster.

We'll do it like that:

  • connect to one of the Docker containers
# docker exec -u redis -ti redis-db1-1 bash
  • create the cluster
    • with 6 Master nodes
# redis-cli --cluster create redis1:6379 redis2:6379 redis3:6379 redis4:6379 redis5:6379 redis6:6379
    • with 3 master and 3 Slave nodes
# redis-cli --cluster create redis1:6379 redis2:6379 redis3:6379 redis4:6379 redis5:6379 redis6:6379 --cluster-replicas 1  
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica redis5:6379 to redis1:6379
Adding replica redis6:6379 to redis2:6379
Adding replica redis4:6379 to redis3:6379
M: 2ac80692b2ccccc1b0f25ca700aaa4265aa2688f redis1:6379
   slots:[0-5460] (5461 slots) master
M: 9884b933acf824f9ea70730aae034e5370bef383 redis2:6379
   slots:[5461-10922] (5462 slots) master
M: ae7efb99e9bc29f4ae26650fdd1c33a0d4801089 redis3:6379
   slots:[10923-16383] (5461 slots) master
S: 46dba1ab8ac4c18a9bf7e2e689d3b2f66caac8e8 redis4:6379
   replicates ae7efb99e9bc29f4ae26650fdd1c33a0d4801089
S: 6191fe21bc34c39a5a012af0f55d4dac3801bd67 redis5:6379
   replicates 2ac80692b2ccccc1b0f25ca700aaa4265aa2688f
S: 3dd391427377c80340cfda9dbb9a4b654a221a91 redis6:6379
   replicates 9884b933acf824f9ea70730aae034e5370bef383
Can I set the above configuration? (type 'yes' to accept): yes

The cluster status will look like that (3 Masters and 3 slaves):

# redis-cli --cluster info redis1:6379
redis1:6379 (2ac80692...) -> 0 keys | 5461 slots | 1 slaves.
172.18.0.28:6379 (9884b933...) -> 0 keys | 5462 slots | 1 slaves.
172.18.0.30:6379 (ae7efb99...) -> 0 keys | 5461 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.

Redis is running, and we can use a simple shell script to generate data and save it inside of Redis. In this case, we run 10 000 times, so 10 000 key value pairs are generated:

# for i in $(seq 1 10000)
  do 
  redis-cli -c SET key$i val$i
  done

redis-cli -c allows to write data not only to the current service, but to all, as data is spreaded across the cluster (not needed for standalone node).

[...]
OK
OK
OK
OK
OK
[...]

One OK for every line. 41 seconds are needed to save it into the cluster.

We can see that it's saved, distributed and balanced:

# redis-cli --cluster info redis1:6379
redis1:6379 (2ac80692...) -> 3331 keys | 5461 slots | 1 slaves.
172.18.0.28:6379 (9884b933...) -> 3341 keys | 5462 slots | 1 slaves.
172.18.0.30:6379 (ae7efb99...) -> 3328 keys | 5461 slots | 1 slaves.
[OK] 10000 keys in 3 masters.
0.61 keys per slot on average.

3331 + 3341 + 3328 = 10 000. And if you configured the cluster using --cluster-replicas 1, the data of each service is additionally replicated to his Slave.