KeyDB is a fork of Redis that serves requests from several threads instead of one, speaks the same protocol and reads the same configuration format, so existing Redis clients and libraries work without changes. Its two main additions are multithreading, which raises throughput on servers with several cores, and active replication, where two nodes replicate to each other and both accept writes. In this tutorial you will run KeyDB on Ubuntu 24.04 with the official Docker image, secure it, tune its threads, set up active replication between two servers and migrate data from Redis.
NoteThe latest KeyDB release is 6.3.4, published in October 2023, and it is compatible with Redis 6.2. KeyDB's APT repository has no packages for Ubuntu 24.04, which is why this guide uses the official
eqalpha/keydbDocker image. Check the project's recent activity on GitHub before choosing it for a new production system.
Prerequisites
To follow this tutorial you need:
- A server running Ubuntu 24.04 LTS with at least 2 GB of RAM and 2 or more CPU cores, for example a CubePath VPS, and a non-root user with
sudoprivileges. - Docker Engine with the Compose plugin. Step 1 installs it.
- For Step 6, a second Ubuntu 24.04 server on the same private network. The guide calls their private addresses
node1_private_ipandnode2_private_ip. - For Step 7, an existing Redis 6 server reachable from the KeyDB server.
Step 1 - Installing Docker
Install Docker Engine from Docker's official repository:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
Verify the installation:
sudo docker run --rm hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
Step 2 - Writing the KeyDB configuration
Create a directory for KeyDB that only root can enter, because the configuration file will contain the password:
sudo install -d -m 700 /opt/keydb
sudo install -d /opt/keydb/data
Generate a long random password. Hex characters avoid any quoting problems in the configuration file:
openssl rand -hex 32
Copy the output, then create the configuration file:
sudo nano /opt/keydb/keydb.conf
bind 0.0.0.0
port 6379
protected-mode yes
requirepass your_keydb_password
dir /data
appendonly yes
appendfsync everysec
save 900 1
save 300 10
maxmemory 1gb
maxmemory-policy allkeys-lru
server-threads 2
Replace your_keydb_password with the value you generated. The settings mean:
bind 0.0.0.0makes KeyDB listen on every interface inside the container. Which interface of the server it is reachable on is decided by Docker in the next step.requirepassforces every client to authenticate.appendonly yeswrites every change to an append-only file, flushed once per second, and thesavelines add periodic snapshots, so a restart loses at most about one second of writes.maxmemoryandallkeys-lrumake KeyDB evict the least recently used keys when it reaches 1 GB, which is what you want for a cache. For data that must never be evicted, usenoevictioninstead and sizemaxmemoryaccordingly.server-threads 2enables two worker threads. Step 4 explains how to tune it.
Step 3 - Starting KeyDB with Docker Compose
Create the Compose file:
sudo nano /opt/keydb/compose.yaml
services:
keydb:
image: eqalpha/keydb:latest
container_name: keydb
restart: unless-stopped
command: ["keydb-server", "/etc/keydb/keydb.conf"]
ports:
- "127.0.0.1:6379:6379"
volumes:
- ./keydb.conf:/etc/keydb/keydb.conf:ro
- ./data:/data
The port is published on 127.0.0.1 only, so applications on this server can connect but nothing outside can. The latest tag points to 6.3.4, the last release.
Start the container:
cd /opt/keydb
sudo docker compose up -d
To avoid typing the password on the command line, read it from the configuration file into a shell variable for this session:
KEYDB_PASSWORD=$(sudo awk '/^requirepass/ {print $2}' /opt/keydb/keydb.conf)
Check that KeyDB answers and that authentication is enforced:
sudo docker exec keydb keydb-cli ping
sudo docker exec keydb keydb-cli --no-auth-warning -a "$KEYDB_PASSWORD" ping
NOAUTH Authentication required.
PONG
Confirm the version and that the data directory is being used:
sudo docker exec keydb keydb-server --version
sudo ls /opt/keydb/data
KeyDB server v=6.3.4 sha=00000000:0 malloc=jemalloc-5.2.1 bits=64 build=...
appendonly.aof
Connecting applications
KeyDB accepts the same connection strings as Redis. An application on this server connects with:
redis://:[email protected]:6379/0
To use the regular Redis command-line client from the host, install redis-tools and pass the password through the REDISCLI_AUTH variable:
sudo apt install redis-tools
REDISCLI_AUTH="$KEYDB_PASSWORD" redis-cli -h 127.0.0.1 set greeting "hello from keydb"
REDISCLI_AUTH="$KEYDB_PASSWORD" redis-cli -h 127.0.0.1 get greeting
OK
"hello from keydb"
Step 4 - Tuning multithreading
server-threads sets how many threads accept and process client requests. KeyDB's own documentation advises relating it to your network throughput rather than to the core count, and not going above 4. A practical rule is 2 threads on a 2 to 4 core server and 4 threads on 8 cores or more, leaving cores free for the operating system and for persistence.
Check the core count and the current setting:
nproc
sudo docker exec keydb keydb-cli --no-auth-warning -a "$KEYDB_PASSWORD" config get server-threads
4
1) "server-threads"
2) "2"
server-threads is read at startup. To change it, edit /opt/keydb/keydb.conf and restart the container with sudo docker compose restart from /opt/keydb.
Measure the effect with the bundled benchmark tool, which opens 50 parallel connections and sends 100,000 SET and GET commands:
sudo docker exec keydb keydb-benchmark -a "$KEYDB_PASSWORD" -c 50 -n 100000 -t set,get -q
SET: 142653.36 requests per second, p50=0.183 msec
GET: 151975.69 requests per second, p50=0.167 msec
Run it with different server-threads values on your own hardware. The benchmark runs inside the same container and competes for CPU, so use it to compare settings rather than as an absolute number. The benchmark writes test keys, so run it before loading real data or on a test instance.
Step 5 - Allowing access from other servers
If applications on other servers need KeyDB, publish the port on the private network interface instead of on 127.0.0.1, and allow only the servers that need it. Never publish KeyDB on a public IP address.
Edit the ports section of /opt/keydb/compose.yaml:
ports:
- "node1_private_ip:6379:6379"
Apply the change and allow the client or peer server in the firewall:
cd /opt/keydb
sudo docker compose up -d
sudo ufw allow from node2_private_ip to any port 6379 proto tcp
WarningPorts published by Docker are opened in iptables directly and are not filtered by UFW rules. The binding to the private address is what keeps KeyDB off the public internet, so double check the
portsline. On a CubePath private network, only your own servers can reach the private address.
Step 6 - Setting up active replication between two nodes
With standard Redis replication, one node accepts writes and the replicas are read-only. With KeyDB's active replication, each node is a replica of the other and both accept writes, so an application can write to whichever node is closer or still alive.
Repeat Steps 1 to 5 on the second server, using the same password on both nodes and publishing the port on each server's private address. Then add the replication settings to /opt/keydb/keydb.conf on node 1:
active-replica yes
replica-read-only no
masterauth your_keydb_password
replicaof node2_private_ip 6379
On node 2, add the same block, pointing to node 1:
active-replica yes
replica-read-only no
masterauth your_keydb_password
replicaof node1_private_ip 6379
masterauth is the password each node uses to authenticate against its peer.
WarningWhen a replica connects, it discards its own dataset and loads the one from its master. If both nodes already hold different data, the first one to sync wins and the other node's keys are lost. Start with node 1 holding the data and node 2 empty, and restart node 1 first.
Restart the container on node 1, then on node 2:
cd /opt/keydb
sudo docker compose restart
Check the replication status on either node:
sudo docker exec keydb keydb-cli --no-auth-warning -a "$KEYDB_PASSWORD" info replication | grep -E 'role|master_host|master_link_status'
role:active-replica
master_host:node2_private_ip
master_link_status:up
Test writes in both directions. On node 1:
sudo docker exec keydb keydb-cli --no-auth-warning -a "$KEYDB_PASSWORD" set written_on node1
On node 2, read that key and write another one:
sudo docker exec keydb keydb-cli --no-auth-warning -a "$KEYDB_PASSWORD" get written_on
sudo docker exec keydb keydb-cli --no-auth-warning -a "$KEYDB_PASSWORD" set reply_from node2
"node1"
OK
Back on node 1, get reply_from returns "node2". Keep in mind that if the same key is written on both nodes at the same moment, the last write to arrive wins; design the application so that each key has a single writer when that matters.
Step 7 - Migrating data from Redis
The least disruptive migration is to make KeyDB a temporary replica of the existing Redis server. KeyDB copies the full dataset and then keeps it in sync until you switch the applications over. This works with Redis 6.x. Redis 7 writes a newer snapshot format that KeyDB 6.3 may not be able to load; if the replication log shows Can't handle RDB format version, you need an export and import tool instead.
On the Redis server, allow the KeyDB server's address to reach port 6379 on its private interface. Then, on the KeyDB server, set the Redis password and start replicating. Replace redis_private_ip and your_redis_password:
sudo docker exec keydb keydb-cli --no-auth-warning -a "$KEYDB_PASSWORD" config set masterauth your_redis_password
sudo docker exec keydb keydb-cli --no-auth-warning -a "$KEYDB_PASSWORD" replicaof redis_private_ip 6379
Wait until the link is up and the key counts match on both sides:
sudo docker exec keydb keydb-cli --no-auth-warning -a "$KEYDB_PASSWORD" info replication | grep master_link_status
sudo docker exec keydb keydb-cli --no-auth-warning -a "$KEYDB_PASSWORD" dbsize
master_link_status:up
(integer) 184233
Compare the number with redis-cli dbsize on the Redis server. Then point your applications at KeyDB, and once no client writes to Redis any more, promote KeyDB to a standalone server:
sudo docker exec keydb keydb-cli --no-auth-warning -a "$KEYDB_PASSWORD" replicaof no one
OK
These config set and replicaof commands change the running server only, so after a restart KeyDB uses /opt/keydb/keydb.conf again, which is what you want once the migration is finished.
Troubleshooting
The container keeps restarting. Read the log with sudo docker compose logs --tail 50 keydb from /opt/keydb. A typo in keydb.conf shows as Bad directive or wrong number of arguments with the line number.
NOAUTH Authentication required from an application. The client is not sending the password. Add it to the connection string or the client options.
master_link_status:down on a replica. Check that the peer is reachable with nc -zv node2_private_ip 6379, that the firewall allows it, and that masterauth matches the peer's requirepass. The container log shows the exact error from the sync attempt.
OOM command not allowed when used memory > 'maxmemory'. The policy is noeviction and memory is full. Raise maxmemory or switch to an eviction policy such as allkeys-lru if the data is a cache.
Conclusion
You now have KeyDB running on Ubuntu 24.04 with authentication, persistence, tuned worker threads and, optionally, active replication between two nodes, and you know how to move data over from Redis without downtime. As next steps, back up /opt/keydb/data regularly, monitor memory with info memory, and test a node failure to see how your application behaves when one of the two replicas disappears.
