Running Redis on Kubernetes#

Redis on Kubernetes ranges from dead simple (single pod for caching) to operationally complex (Redis Cluster with persistence). The right choice depends on whether you need data durability, high availability, or just a fast throwaway cache.

Single-Instance Redis with Persistence#

For development or small workloads, a single Redis Deployment with a PVC is enough:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7-alpine
        command: ["redis-server", "--appendonly", "yes", "--maxmemory", "256mb", "--maxmemory-policy", "allkeys-lru"]
        ports:
        - containerPort: 6379
        volumeMounts:
        - name: redis-data
          mountPath: /data
        resources:
          requests:
            cpu: 100m
            memory: 300Mi
          limits:
            cpu: 500m
            memory: 350Mi
      volumes:
      - name: redis-data
        persistentVolumeClaim:
          claimName: redis-data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redis-data
spec:
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: Service
metadata:
  name: redis
spec:
  selector:
    app: redis
  ports:
  - port: 6379
    targetPort: 6379

Set the memory limit in Redis (--maxmemory) lower than the container memory limit. If Redis uses 350Mi and the container limit is 350Mi, the kernel OOM-kills the process during background save operations when Redis forks and temporarily doubles its memory usage. A safe ratio: set maxmemory to 60-75% of the container memory limit.

Bitnami Redis Helm Chart#

The Bitnami chart handles most operational concerns out of the box. Three architecture modes are available.

Standalone:

helm repo add bitnami https://charts.bitnami.com/bitnami
helm install redis bitnami/redis --set architecture=standalone --set auth.password=your-secure-password

Replication (master + read replicas):

# redis-values.yaml
architecture: replication
auth:
  password: "your-secure-password"
master:
  persistence:
    size: 10Gi
  resources:
    requests:
      memory: 256Mi
      cpu: 100m
    limits:
      memory: 512Mi
replica:
  replicaCount: 2
  persistence:
    size: 10Gi
  resources:
    requests:
      memory: 256Mi
      cpu: 100m
    limits:
      memory: 512Mi
helm install redis bitnami/redis -f redis-values.yaml

This creates redis-master and redis-replicas Services. Your application writes to redis-master:6379 and reads from redis-replicas:6379.

Sentinel (automatic failover):

architecture: replication
sentinel:
  enabled: true
  masterSet: mymaster
  quorum: 2
  resources:
    requests:
      memory: 64Mi
      cpu: 50m

With Sentinel, clients connect to port 26379 to discover the current master. Most Redis client libraries support Sentinel natively with the master set name mymaster.

Memory and Eviction Configuration#

The two most important Redis settings for Kubernetes:

maxmemory: The maximum bytes Redis will use before evicting keys. Without this, Redis grows unbounded until the OOM killer strikes.

maxmemory-policy: What happens when maxmemory is reached:

Policy Behavior
noeviction Returns errors on writes when full. Good for data you cannot lose.
allkeys-lru Evicts least recently used keys. Best for general caching.
volatile-lru Evicts LRU keys that have a TTL set. Keys without TTL are never evicted.
allkeys-random Random eviction. Slightly faster than LRU.
volatile-ttl Evicts keys with the shortest TTL first.

For caching, use allkeys-lru. For session storage where some keys are permanent, use volatile-lru. For queues or data stores, use noeviction and monitor memory closely.

Set these via Helm values:

master:
  configuration: |
    maxmemory 256mb
    maxmemory-policy allkeys-lru
    save 900 1
    save 300 10
    appendonly yes
    appendfsync everysec

Persistent Storage Considerations#

Redis has two persistence mechanisms: RDB snapshots (save directives) and AOF (appendonly yes). RDB is fast to recover but loses data since the last snapshot. AOF logs every write – with appendfsync everysec you lose at most one second. Enable both for production and ensure the data directory is on the PVC mount (/data). Use SSD-backed StorageClasses – AOF write performance depends directly on disk latency.

Monitoring with redis-cli#

kubectl exec -it deploy/redis -- redis-cli -a your-password
INFO memory       # used_memory_human, maxmemory_human, mem_fragmentation_ratio
INFO persistence  # rdb_last_bgsave_status, aof_last_bgrewrite_status
INFO stats        # instantaneous_ops_per_sec
SLOWLOG GET 10    # slow commands
INFO keyspace     # key counts

A mem_fragmentation_ratio above 1.5 means Redis is wasting memory, often from many small key deletions. A restart or MEMORY PURGE can help.

Common Issues#

OOM Killed. Pod killed with exit code 137. Causes: maxmemory not set, set too close to the container limit, or a background save fork doubling memory. Fix: set maxmemory to 60-75% of the container memory limit.

AOF rewrite blocking. Large AOF rewrites can block the main process on slow disks. Symptoms: latency spikes, client timeouts. Mitigate with SSD StorageClasses and tuning auto-aof-rewrite-percentage.

Slow commands. Redis is single-threaded. A KEYS * on a large database blocks everything. Use SCAN instead and consider rename-command KEYS "" in production.

Replica sync failures after restart. When a replica reconnects and the replication backlog has been overwritten, it falls back to a full sync, dumping the entire dataset. Increase repl-backlog-size if replicas frequently disconnect.

CrashLoopBackOff from data corruption. If the AOF file is corrupted, Redis refuses to start. Fix with redis-check-aof --fix /data/appendonlydir/appendonly.aof.1.incr.aof from a debug container.