Redis: In-Memory Caching for Homelab Apps
High-performance caching and message broker. Speed up your apps with Redis.
Introduction
Redis (Remote Dictionary Server) is an open-source, in-memory data structure store used as a database, cache, message broker, and streaming engine. With microsecond response times and support for rich data structures, Redis has become the de facto standard for caching and real-time applications.
For homelabbers, Redis can dramatically improve application performance, enable session management across services, and provide pub/sub messaging for home automation and monitoring systems.
What You’ll Learn:
- Deploy Redis with Docker in single and clustered configurations
- Install and configure Redis Insight for visual management
- Implement persistence strategies (RDB snapshots and AOF)
- Configure Redis as a cache, database, and message broker
- Set up Redis Sentinel for high availability
- Secure Redis with authentication and encryption
- Monitor performance with metrics and slow query logs
- Integrate Redis with common homelab applications
Prerequisites:
- Docker and Docker Compose installed
- Basic understanding of key-value stores
- Command-line proficiency
- Homelab server with minimum 1GB RAM, 10GB storage
- Recommended: 4GB+ RAM for production caching workloads
Part 1: Understanding Redis
What is Redis?
Redis is an in-memory data structure store that persists data to disk. Unlike traditional databases that store data on disk and cache in memory, Redis keeps the entire dataset in RAM for ultra-fast access while optionally persisting to disk for durability.
Key Characteristics:
- In-Memory Storage: All data stored in RAM for microsecond latency
- Rich Data Structures: Strings, hashes, lists, sets, sorted sets, bitmaps, streams
- Persistence Options: RDB snapshots and/or AOF (Append-Only File)
- Atomic Operations: All operations are atomic, even complex ones
- Pub/Sub Messaging: Built-in publish/subscribe capabilities
- Lua Scripting: Server-side scripting for complex operations
- Replication: Master-replica with automatic failover via Sentinel
- Clustering: Horizontal scaling with automatic sharding
Why Redis for Homelabs?
Technical Advantages:
Blazing Fast Performance
- Sub-millisecond response times (typically under 1ms)
- 100,000+ operations per second on modest hardware
- Perfect for real-time applications
Versatile Data Structures
- String: Simple key-value pairs
- Hash: Store objects with multiple fields
- List: Ordered collections, perfect for queues
- Set: Unique values, great for tags
- Sorted Set: Ranked data like leaderboards
- Streams: Log-like data structures for event streaming
Flexible Use Cases
- Session storage for web applications
- API rate limiting
- Real-time analytics
- Job queues (Celery, Bull, Sidekiq)
- Cache layer for databases
- Pub/Sub for microservices communication
Easy to Deploy
- Single binary, minimal configuration
- Runs well on Raspberry Pi and low-power devices
- Docker-friendly
Common Homelab Use Cases:
- Application Caching: Speed up Nextcloud, Immich, Authentik, Gitea
- Session Management: Centralized sessions for multiple web apps
- Rate Limiting: Protect APIs from abuse
- Home Assistant: State storage and automation triggers
- Monitoring: Temporary metrics storage (before writing to InfluxDB)
- Job Queues: Background task processing
- Real-Time Analytics: Track website visitors, API calls
- Message Broker: Inter-service communication
Redis vs. Alternatives
| Feature | Redis | Memcached | KeyDB | Valkey |
|---|---|---|---|---|
| Data Structures | Many | Strings only | Many | Many |
| Persistence | Yes | No | Yes | Yes |
| Replication | Yes | No | Yes | Yes |
| Clustering | Yes | Client-side | Yes | Yes |
| Pub/Sub | Yes | No | Yes | Yes |
| License | BSD | BSD | BSD | BSD |
| Multithreading | Limited | Yes | Yes | Yes |
| Performance | Excellent | Excellent | Faster | Faster |
| Maturity | Very High | High | Medium | New |
| Best For | Full-featured | Simple cache | High perf | Redis fork |
Note: KeyDB and Valkey are Redis forks with multi-threading and other enhancements. They’re drop-in replacements with better performance on multi-core systems.
Redis Architecture
Single Instance:
Redis Server Process
├── Event Loop (single-threaded)
│ ├── Network I/O
│ ├── Command Processing
│ └── Client Response
├── Memory (Data Structures)
│ ├── Key Space
│ ├── Expires (TTL tracking)
│ └── Pub/Sub Channels
└── Persistence
├── RDB Writer (background)
└── AOF Writer (append-only) How Redis Handles Commands:
Client Request → TCP Connection → Parse Command
↓
Execute Atomically → Modify In-Memory Data
↓
Optional: Write to AOF → Reply to Client
↓
Background: RDB Snapshot (if configured) Part 2: Installing Redis with Docker
Quick Start Deployment
Create project directory:
mkdir -p ~/redis-homelab
cd ~/redis-homelab Create docker-compose.yml:
version: '3.8'
services:
redis:
image: redis:7-alpine
container_name: redis
hostname: redis
restart: unless-stopped
command: >
redis-server
--requirepass ${REDIS_PASSWORD:-ChangeMe123!}
--appendonly yes
--appendfsync everysec
--maxmemory 512mb
--maxmemory-policy allkeys-lru
--save 900 1
--save 300 10
--save 60 10000
ports:
- "6379:6379"
volumes:
# Data persistence
- redis-data:/data
# Custom configuration (optional)
# - ./redis.conf:/usr/local/etc/redis/redis.conf:ro
networks:
- redis-network
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
interval: 30s
timeout: 10s
retries: 5
start_period: 20s
# Resource limits
deploy:
resources:
limits:
cpus: '1'
memory: 1G
reservations:
cpus: '0.25'
memory: 256M
volumes:
redis-data:
driver: local
driver_opts:
type: none
o: bind
device: /mnt/storage/redis-data
networks:
redis-network:
driver: bridge Create secure password (.env):
# Generate strong password
REDIS_PASSWORD=$(openssl rand -base64 32)
# Save to .env
cat > .env <<EOF
REDIS_PASSWORD=$REDIS_PASSWORD
EOF
chmod 600 .env
echo "Redis password: $REDIS_PASSWORD"
echo "Save this password securely!" Deploy Redis:
# Start Redis
docker-compose up -d
# View logs
docker-compose logs -f redis
# Verify status
docker-compose ps
docker exec redis redis-cli -a "${REDIS_PASSWORD}" ping
# Should return: PONG Connect to Redis CLI:
# Using redis-cli in container
docker exec -it redis redis-cli -a "${REDIS_PASSWORD}"
# Once connected, try these commands:
PING # Test connection
SET mykey "Hello" # Set a value
GET mykey # Get the value
KEYS * # List all keys (avoid in production!)
INFO # Server info
EXIT # Quit Redis with Redis Insight
Extended docker-compose.yml:
version: '3.8'
services:
redis:
# ... (previous redis config)
redis-insight:
image: redislabs/redisinsight:latest
container_name: redis-insight
restart: unless-stopped
ports:
- "8001:8001"
volumes:
- redis-insight-data:/db
networks:
- redis-network
depends_on:
redis:
condition: service_healthy
volumes:
redis-data:
driver: local
redis-insight-data:
driver: local
networks:
redis-network:
driver: bridge Start both services:
docker-compose up -d Access Redis Insight at http://your-server-ip:8001
Configure connection in Redis Insight:
- Click “Add Redis Database”
- Host:
redis - Port:
6379 - Password: Your Redis password
- Click “Add Redis Database”
Part 3: Configuration and Persistence
Custom Configuration File
Create redis.conf:
# Network
bind 0.0.0.0
port 6379
protected-mode yes
tcp-backlog 511
timeout 0
tcp-keepalive 300
# General
daemonize no
supervised no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
databases 16
# Security
requirepass your_redis_password_here
# rename-command FLUSHDB ""
# rename-command FLUSHALL ""
# rename-command CONFIG ""
# Memory Management
maxmemory 512mb
maxmemory-policy allkeys-lru
# maxmemory-policy options:
# noeviction - Return errors when memory limit is reached
# allkeys-lru - Evict any key using LRU (Least Recently Used)
# volatile-lru - Evict keys with TTL using LRU
# allkeys-random - Evict random keys
# volatile-random - Evict random keys with TTL
# volatile-ttl - Evict keys with shortest TTL
# Persistence - RDB (Snapshots)
save 900 1 # Save after 900 sec (15 min) if at least 1 key changed
save 300 10 # Save after 300 sec (5 min) if at least 10 keys changed
save 60 10000 # Save after 60 sec if at least 10000 keys changed
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /data
# Persistence - AOF (Append Only File)
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
# appendfsync options:
# always - Fsync every write (slow, safest)
# everysec - Fsync every second (good balance)
# no - Let OS decide when to fsync (fastest, least safe)
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
# Replication (if using master-replica)
# replicaof YOUR-SERVER-IP 6379
# masterauth your_redis_password_here
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
# Security - Command Renaming (optional hardening)
# rename-command FLUSHDB ""
# rename-command FLUSHALL ""
# rename-command KEYS ""
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
# Slow Log
slowlog-log-slower-than 10000
slowlog-max-len 128
# Latency Monitor
latency-monitor-threshold 100
# Event Notification
notify-keyspace-events ""
# Advanced Config
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
# Active Rehashing
activerehashing yes
# Client Output Buffers
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
# Frequency
hz 10
dynamic-hz yes
# AOF Rewrite
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes Use custom config:
# In docker-compose.yml
services:
redis:
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf:ro
command: redis-server /usr/local/etc/redis/redis.conf Understanding Persistence Strategies
RDB (Redis Database Backup):
Pros:
- Point-in-time snapshots
- Compact single-file backups
- Faster restarts (binary format)
- Good for disaster recovery
Cons:
- Can lose data between snapshots
- Fork() can pause Redis on large datasets
- Not ideal for write-heavy workloads
AOF (Append-Only File):
Pros:
- More durable (minimal data loss)
- Append-only, less corruption risk
- Automatic log rewriting
- Human-readable (can be edited)
Cons:
- Larger file sizes
- Slower restarts
- Slightly slower writes with fsync
Recommended Strategy:
# Use both for maximum durability
appendonly yes
appendfsync everysec
save 900 1
save 300 10
save 60 10000 Memory Eviction Policies
Choose based on your use case:
1. Cache Only (LRU):
maxmemory 512mb
maxmemory-policy allkeys-lru Best for: Caching database queries, API responses
2. Cache with TTLs:
maxmemory 512mb
maxmemory-policy volatile-lru Best for: Session storage, temporary data
3. Never Evict (Database Mode):
maxmemory 512mb
maxmemory-policy noeviction Best for: Job queues, critical data Warning: Redis will return errors when memory is full
4. Random Eviction:
maxmemory 512mb
maxmemory-policy allkeys-random Best for: When all keys have equal importance
Part 4: Common Use Cases and Patterns
1. Application Caching
Cache database queries:
# Set cache with 1-hour expiration
SET user:1000:profile '{"name":"John","email":"[email protected]"}' EX 3600
# Get cached data
GET user:1000:profile
# Check if key exists
EXISTS user:1000:profile
# Delete cache (invalidation)
DEL user:1000:profile
# Pattern: Cache-aside
# 1. Check cache first
# 2. If miss, query database
# 3. Store result in cache
# 4. Return data Python example (cache-aside pattern):
import redis
import json
# Connect to Redis
r = redis.Redis(host='localhost', port=6379, password='your_password', decode_responses=True)
def get_user_profile(user_id):
# Check cache
cache_key = f"user:{user_id}:profile"
cached = r.get(cache_key)
if cached:
return json.loads(cached)
# Cache miss - query database
user_data = database.query(f"SELECT * FROM users WHERE id = {user_id}")
# Store in cache for 1 hour
r.setex(cache_key, 3600, json.dumps(user_data))
return user_data 2. Session Storage
Store user sessions:
# Create session (24-hour expiration)
HSET session:abc123 user_id 1000
HSET session:abc123 username "john"
HSET session:abc123 last_seen "2024-12-16T10:30:00"
EXPIRE session:abc123 86400
# Get session data
HGETALL session:abc123
# Update last seen
HSET session:abc123 last_seen "2024-12-16T11:00:00"
# Delete session (logout)
DEL session:abc123 3. Rate Limiting
Limit API requests:
# Simple rate limiting (10 requests per minute)
# Redis key: rate_limit:IP_ADDRESS
# Increment counter
INCR rate_limit:192.168.1.100
# Set expiration on first request
EXPIRE rate_limit:192.168.1.100 60
# Check current count
GET rate_limit:192.168.1.100 Python rate limiter:
def check_rate_limit(ip_address, max_requests=10, window=60):
key = f"rate_limit:{ip_address}"
# Get current count
count = r.incr(key)
# Set expiration on first request
if count == 1:
r.expire(key, window)
# Check if over limit
if count > max_requests:
return False # Rate limited
return True # Allowed 4. Job Queue
Simple job queue with lists:
# Producer: Add job to queue
LPUSH jobs:email '{"to":"[email protected]","subject":"Welcome"}'
# Consumer: Get job from queue (blocking)
BRPOP jobs:email 0
# Get queue length
LLEN jobs:email
# Peek at next job without removing
LINDEX jobs:email -1 5. Pub/Sub Messaging
Publish/Subscribe pattern:
# Subscriber (in one terminal)
SUBSCRIBE homeassistant:events
# Publisher (in another terminal)
PUBLISH homeassistant:events '{"type":"motion_detected","sensor":"living_room"}'
# Pattern subscription
PSUBSCRIBE homeassistant:* Python pub/sub example:
# Publisher
def publish_event(channel, message):
r.publish(channel, json.dumps(message))
# Subscriber
def event_listener():
pubsub = r.pubsub()
pubsub.subscribe('homeassistant:events')
for message in pubsub.listen():
if message['type'] == 'message':
data = json.loads(message['data'])
print(f"Event received: {data}") 6. Leaderboard (Sorted Sets)
Store ranked data:
# Add scores
ZADD leaderboard 100 "player1"
ZADD leaderboard 250 "player2"
ZADD leaderboard 180 "player3"
# Get top 10
ZREVRANGE leaderboard 0 9 WITHSCORES
# Get player rank
ZREVRANK leaderboard "player2"
# Increment score
ZINCRBY leaderboard 50 "player1"
# Get score range
ZRANGEBYSCORE leaderboard 100 200 WITHSCORES Part 5: High Availability with Sentinel
Understanding Redis Sentinel
Redis Sentinel provides:
- Monitoring: Checks if master and replicas are working
- Notification: Alerts when something goes wrong
- Automatic Failover: Promotes replica to master if master fails
- Configuration Provider: Clients discover current master address
Sentinel Architecture:
Sentinel 1 ←→ Sentinel 2 ←→ Sentinel 3
↓ ↓ ↓
Redis Master ← Redis Replica 1
← Redis Replica 2 Deploying Redis with Sentinel
Create sentinel-compose.yml:
version: '3.8'
services:
redis-master:
image: redis:7-alpine
container_name: redis-master
command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
ports:
- "6379:6379"
volumes:
- redis-master-data:/data
networks:
redis-ha-network:
ipv4_address: 172.30.0.10
redis-replica-1:
image: redis:7-alpine
container_name: redis-replica-1
command: >
redis-server
--replicaof redis-master 6379
--requirepass ${REDIS_PASSWORD}
--masterauth ${REDIS_PASSWORD}
--appendonly yes
ports:
- "6380:6379"
volumes:
- redis-replica-1-data:/data
networks:
redis-ha-network:
ipv4_address: 172.30.0.11
depends_on:
- redis-master
redis-replica-2:
image: redis:7-alpine
container_name: redis-replica-2
command: >
redis-server
--replicaof redis-master 6379
--requirepass ${REDIS_PASSWORD}
--masterauth ${REDIS_PASSWORD}
--appendonly yes
ports:
- "6381:6379"
volumes:
- redis-replica-2-data:/data
networks:
redis-ha-network:
ipv4_address: 172.30.0.12
depends_on:
- redis-master
sentinel-1:
image: redis:7-alpine
container_name: sentinel-1
command: >
redis-sentinel /etc/redis/sentinel.conf
--sentinel
ports:
- "26379:26379"
volumes:
- ./sentinel-1.conf:/etc/redis/sentinel.conf
networks:
redis-ha-network:
ipv4_address: 172.30.0.20
depends_on:
- redis-master
sentinel-2:
image: redis:7-alpine
container_name: sentinel-2
command: redis-sentinel /etc/redis/sentinel.conf --sentinel
ports:
- "26380:26379"
volumes:
- ./sentinel-2.conf:/etc/redis/sentinel.conf
networks:
redis-ha-network:
ipv4_address: 172.30.0.21
depends_on:
- redis-master
sentinel-3:
image: redis:7-alpine
container_name: sentinel-3
command: redis-sentinel /etc/redis/sentinel.conf --sentinel
ports:
- "26381:26379"
volumes:
- ./sentinel-3.conf:/etc/redis/sentinel.conf
networks:
redis-ha-network:
ipv4_address: 172.30.0.22
depends_on:
- redis-master
volumes:
redis-master-data:
redis-replica-1-data:
redis-replica-2-data:
networks:
redis-ha-network:
driver: bridge
ipam:
config:
- subnet: 172.30.0.0/16 Create sentinel.conf:
port 26379
dir /tmp
sentinel monitor mymaster redis-master 6379 2
sentinel auth-pass mymaster your_redis_password
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 10000 Test failover:
# Stop master
docker stop redis-master
# Check sentinel logs
docker logs -f sentinel-1
# Sentinels will promote a replica to master
# Verify new master
docker exec redis-replica-1 redis-cli -a "${REDIS_PASSWORD}" ROLE Part 6: Monitoring and Performance
Essential Redis Commands
Server info:
# Comprehensive server information
INFO
# Specific sections
INFO server
INFO clients
INFO memory
INFO persistence
INFO stats
INFO replication
INFO cpu
INFO keyspace
# Memory usage
MEMORY STATS
MEMORY DOCTOR
# Client list
CLIENT LIST
# Slow log
SLOWLOG GET 10
SLOWLOG LEN
SLOWLOG RESET Performance monitoring:
# Monitor commands in real-time
MONITOR
# Latency diagnostics
LATENCY DOCTOR
LATENCY GRAPH
LATENCY HISTORY
# Stats
INFO stats Prometheus Redis Exporter
Add to docker-compose.yml:
services:
redis-exporter:
image: oliver006/redis_exporter:latest
container_name: redis-exporter
environment:
REDIS_ADDR: redis:6379
REDIS_PASSWORD: ${REDIS_PASSWORD}
ports:
- "9121:9121"
networks:
- redis-network
depends_on:
- redis Prometheus config:
# prometheus.yml
scrape_configs:
- job_name: 'redis'
static_configs:
- targets: ['redis-exporter:9121'] Grafana Dashboard:
Import Dashboard ID: 11835 (Redis Dashboard for Prometheus)
Part 7: Security Best Practices
1. Enable Password Authentication
requirepass your_strong_password_here 2. Bind to Specific Interface
# Only local connections
bind 127.0.0.1
# Specific network
bind 192.168.1.100 3. Disable Dangerous Commands
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command KEYS ""
rename-command CONFIG "CONFIG_b840fc02d524045429" 4. Enable TLS/SSL
port 0
tls-port 6379
tls-cert-file /path/to/redis.crt
tls-key-file /path/to/redis.key
tls-ca-cert-file /path/to/ca.crt 5. Network Isolation
Use Docker networks to isolate Redis from untrusted containers.
Conclusion
You now have a comprehensive understanding of Redis deployment and management in your homelab.
Key Takeaways:
- Performance: In-memory storage provides microsecond latency
- Versatility: Multiple data structures for different use cases
- Persistence: RDB and AOF options for durability
- High Availability: Sentinel for automatic failover
- Easy Integration: Works with virtually all programming languages
Next Steps:
- Integrate Redis caching into your applications
- Set up monitoring and alerting
- Implement Redis Cluster for horizontal scaling
- Explore Redis Streams for event processing
- Practice disaster recovery procedures
Resources:
- Redis Official Documentation
- Redis Commands Reference
- Redis University (Free Courses)
- r/Redis Community
Happy caching!
Guide Version: 1.0 Last Updated: December 2024 Target Audience: Homelab administrators, developers Difficulty Level: Intermediate Estimated Time: 45 minutes