Valkey on Linux: Production Redis-Compatible Caching and Queues
Database

Valkey on Linux: Production Redis-Compatible Caching and Queues

  • AuthorTimothy M.
  • Date
  • Time26 minutes

Valkey is an open-source, Redis-compatible in-memory datastore maintained by the Linux Foundation. It supports the same protocol, data structures, and client libraries as Redis — which makes it a practical choice for session storage, API caching, rate limiting, pub/sub, and job queues. Teams migrating from Redis or starting fresh benefit from a permissive license and an active community without changing application code in most cases.

This guide focuses on a single-node production baseline on Ubuntu or Debian. You will configure persistence, memory eviction, ACL-based authentication, and operational monitoring. The patterns here apply equally whether Valkey runs on a VPS, dedicated server, or as a sidecar next to your application stack.

When to Use Valkey

  • Application caching — reduce database load for read-heavy endpoints.
  • Session storage — share login state across multiple app instances.
  • Rate limiting — atomic counters with TTL for API throttling.
  • Job queues — Bull, Sidekiq-compatible patterns via lists and streams.
  • Real-time features — pub/sub for notifications and live dashboards.

Valkey is not a replacement for PostgreSQL or MySQL. Treat it as a fast, ephemeral layer with optional persistence — never as your system of record unless you fully understand the durability trade-offs of RDB snapshots and AOF logs.

Prerequisites

  • Ubuntu 22.04+ or Debian 12 with at least 2 GB RAM (4 GB+ recommended)
  • SSD storage for AOF/RDB files if persistence is enabled
  • Application already using a Redis client library (ioredis, redis-py, phpredis, etc.)
  • Firewall configured — Valkey should not be exposed to the public internet

Step 1: Install Valkey from Official Packages

Use the maintained packages rather than compiling from source unless you need a specific build flag. Package installs integrate cleanly with systemd and simplify security updates.

bash
curl -fsSL https://packages.valkey.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/valkey-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/valkey-archive-keyring.gpg] https://packages.valkey.io/deb stable main" | sudo tee /etc/apt/sources.list.d/valkey.list
sudo apt update
sudo apt install -y valkey
valkey-server --version

Step 2: Harden the Base Configuration

The default config often binds to all interfaces and has no password. For production, bind to localhost or a private network interface, disable dangerous commands, and enable ACL authentication.

bash
sudo cp /etc/valkey/valkey.conf /etc/valkey/valkey.conf.bak

sudo tee -a /etc/valkey/valkey.conf >/dev/null <<'EOF'

# --- production overrides ---
bind 127.0.0.1 ::1
protected-mode yes
port 6379
tcp-backlog 511
timeout 300
tcp-keepalive 300

supervised systemd
daemonize no
pidfile /run/valkey/valkey-server.pid
loglevel notice
logfile /var/log/valkey/valkey-server.log

# Memory
maxmemory 2gb
maxmemory-policy allkeys-lru

# Persistence (AOF recommended for durability)
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# Disable dangerous commands
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command DEBUG ""
rename-command CONFIG ""
EOF

Step 3: Configure ACL Users

ACLs replace a single shared password with per-application credentials and command restrictions. Create a dedicated user for each service rather than reusing the default account.

bash
sudo tee /etc/valkey/users.acl >/dev/null <<'EOF'
user default on nopass ~* &* -@all

user app-cache on >REPLACE_APP_CACHE_PASSWORD ~cache:* &* +@read +@write +@string +@hash +@list +@set +@sortedset -@dangerous
user app-queue on >REPLACE_APP_QUEUE_PASSWORD ~queue:* &* +@read +@write +@list +@stream -@dangerous
user admin on >REPLACE_ADMIN_PASSWORD ~* &* +@all
EOF

# Add to valkey.conf:
# aclfile /etc/valkey/users.acl

Step 4: Start and Enable Valkey

bash
sudo systemctl enable valkey
sudo systemctl restart valkey
sudo systemctl status valkey --no-pager
valkey-cli -u redis://app-cache:REPLACE_APP_CACHE_PASSWORD@127.0.0.1:6379 ping

Step 5: Connect from Node.js (ioredis)

javascript
import Redis from 'ioredis';

const cache = new Redis({
  host: '127.0.0.1',
  port: 6379,
  username: 'app-cache',
  password: process.env.VALKEY_CACHE_PASSWORD,
  keyPrefix: 'cache:',
  maxRetriesPerRequest: 3,
  lazyConnect: true,
});

await cache.connect();
await cache.set('user:42', JSON.stringify({ name: 'Alice' }), 'EX', 3600);
const user = JSON.parse(await cache.get('user:42'));

Step 6: Connect from Python (redis-py)

python
import redis

client = redis.Redis(
    host='127.0.0.1',
    port=6379,
    username='app-cache',
    password='REPLACE_APP_CACHE_PASSWORD',
    decode_responses=True,
)

client.setex('cache:session:abc', 1800, '{"user_id": 42}')
print(client.get('cache:session:abc'))

Step 7: Set Up Replication (Primary + Replica)

Replication provides read scaling and faster recovery. For automatic failover, add Sentinel or an orchestrator — replication alone does not promote a replica when the primary fails.

bash
# On the replica server, add to valkey.conf:
# replicaof 10.0.0.10 6379
# masterauth REPLACE_ADMIN_PASSWORD
# replica-read-only yes

sudo systemctl restart valkey
valkey-cli info replication

Step 8: Monitor Key Metrics

bash
valkey-cli info memory
valkey-cli info stats
valkey-cli info persistence
valkey-cli --latency -i 1

# Useful fields to alert on:
# used_memory_rss vs maxmemory
# evicted_keys (should stay near zero for cache-heavy workloads)
# aof_last_write_status
# connected_clients sudden spikes

Memory Policy Guide

  • allkeys-lru — evict any key by LRU when memory is full. Best general-purpose cache policy.
  • volatile-lru — evict only keys with TTL set. Safer when some keys must never expire.
  • noeviction — return errors when memory is full. Use for queues where losing writes is unacceptable.
  • allkeys-lfu — evict least frequently used keys. Good for skewed access patterns.

Troubleshooting

  • OOM or high RSS — lower maxmemory, audit large keys with --bigkeys, enable key TTLs.
  • Slow responses — check SLOWLOG GET 10, avoid KEYS * in production, use SCAN instead.
  • Connection refused — verify bind address, ACL credentials, and that the app connects via private network.
  • AOF rewrite blocking — ensure sufficient disk I/O and consider appendfsync everysec over always.

Production Checklist

  • Never expose port 6379 to the public internet.
  • Use ACL users with least-privilege command sets per application.
  • Set maxmemory below available RAM to leave headroom for the OS and AOF rewrite.
  • Include Valkey data directory in backup strategy if persistence is enabled.
  • Run valkey-cli --bigkeys monthly to find unexpectedly large keys.
  • Document failover procedure before you need it under incident pressure.

"An in-memory datastore is fast because it is ephemeral by design — treat persistence as a safety net, not a substitute for your primary database."

Technical Author

Timothy M.

Timothy M.

Support and server administration. Writes the work on the host: packages, services, and what to check when a machine misbehaves.