Logo

Redis cache for web applications

Redis cache for web applications

Install and configure Redis to cache slow queries and speed up your web apps. This guide covers install, security, cache patterns (cache-aside, write-through) and integration with WordPress, Symfony and Laravel.

Introduction

Redis is an in-memory key/value database, extremely fast (often < 1ms per operation). It's used as:

  • Application cache: API responses, HTML fragments, costly SQL queries
  • Session store: distributed PHP/Node sessions
  • Queue / Pub-Sub: async tasks
  • Rate limiting: distributed counters

This guide focuses on the cache use case, the most immediate one: turning a 200ms SQL query into a 0.5ms Redis read.

Prerequisites

  • Linux VPS (Debian 12 / Ubuntu 22.04+)
  • 512 MB RAM minimum (1 GB recommended)
  • A web app to accelerate

Step 1: Installation

sudo apt update
sudo apt install -y redis-server
sudo systemctl enable --now redis-server
sudo systemctl status redis-server

Test:

redis-cli ping
# PONG

Step 2: Secure Redis

By default, Redis listens only on 127.0.0.1 (localhost). It's safe if your app is on the same VPS. Otherwise, you absolutely must set a password and restrict bind addresses.

sudo nano /etc/redis/redis.conf

Edit:

# Bind addresses (keep localhost if app on same VPS)
bind 127.0.0.1 ::1

# Strong password
requirepass AVeryLongPassword_WithNumbers_42_AndSpecials!

# Protected mode (disable auth-less access)
protected-mode yes

# Rename dangerous commands (optional but recommended)
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command KEYS ""
rename-command CONFIG "CONFIG_zP9aXkLm"

Restart:

sudo systemctl restart redis-server

Test with auth:

redis-cli -a AVeryLongPassword_WithNumbers_42_AndSpecials! ping

Step 3: Configure max memory and eviction policy

sudo nano /etc/redis/redis.conf
# Redis memory limit (adapt to available RAM)
maxmemory 1gb

# Strategy when memory is full
# allkeys-lru: removes least recently used keys
maxmemory-policy allkeys-lru

Available strategies:

  • noeviction: refuses writes when full (DB)
  • allkeys-lru: LRU on all keys ⭐ recommended for cache
  • volatile-lru: LRU on keys with TTL
  • allkeys-random: random
  • volatile-ttl: expires keys with shortest TTL

Step 4: Disable persistence for pure cache

If Redis is purely cache (rebuildable data), disable persistence for perf gains:

# Disable RDB snapshots
save ""

# Disable AOF
appendonly no

⚠️ If Redis is a session store or holds critical data, keep persistence active (save 900 1, appendonly yes).

Step 5: System optimizations for Redis

Redis warns on install:

WARNING: The TCP backlog setting of 511 cannot be enforced
WARNING: overcommit_memory is set to 0
WARNING: Transparent Huge Pages enabled

Fix:

sudo nano /etc/sysctl.conf
net.core.somaxconn = 1024
vm.overcommit_memory = 1
sudo sysctl -p

Disable Transparent Huge Pages:

echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled

To persist:

sudo nano /etc/rc.local
#!/bin/bash
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
exit 0
sudo chmod +x /etc/rc.local

Restart Redis:

sudo systemctl restart redis-server

Step 6: Test performance

# Included benchmark
redis-benchmark -a AVeryLongPassword... -q

Typical output:

SET: 95238 req/s
GET: 103626 req/s
INCR: 102040 req/s
LPUSH: 99009 req/s

100k req/s on a standard VPS. Compared to MySQL (5-10k req/s on simple SELECT), that's 10-20x faster.

Step 7: Cache-Aside Pattern (most common)

Here's how to use it in a PHP app:

<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->auth('AVeryLongPassword...');

function getUser($id) {
    global $redis, $pdo;
    
    $cacheKey = "user:$id";
    
    // 1. Check cache
    $cached = $redis->get($cacheKey);
    if ($cached) {
        return json_decode($cached, true);
    }
    
    // 2. Cache miss: go to DB
    $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->execute([$id]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);
    
    // 3. Store in cache (TTL 1h)
    $redis->setex($cacheKey, 3600, json_encode($user));
    
    return $user;
}

// Invalidate cache on update
function updateUser($id, $data) {
    global $redis, $pdo;
    
    $stmt = $pdo->prepare("UPDATE users SET ... WHERE id = ?");
    $stmt->execute([...]);
    
    // Invalidation
    $redis->del("user:$id");
}

Step 8: WordPress integration

Install the Redis Object Cache plugin:

  1. Plugins → Add New → "Redis Object Cache" → Activate
  2. Settings → Redis:
    • Host: 127.0.0.1
    • Port: 6379
    • Password: your Redis password
    • Database: 0
  3. Click Enable Object Cache

Typical benefit: -50 to -70% load time.

Step 9: Symfony integration

In config/packages/cache.yaml:

framework:
    cache:
        app: cache.adapter.redis
        default_redis_provider: 'redis://default:[email protected]:6379'

And use in code:

public function show(CacheInterface $cache, int $id): Response
{
    $product = $cache->get('product_' . $id, function (ItemInterface $item) use ($id) {
        $item->expiresAfter(3600);
        return $this->productRepo->find($id);
    });
    // ...
}

Step 10: Laravel integration

In .env:

CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=AVeryLongPassword_WithNumbers_42_AndSpecials!
REDIS_PORT=6379

And:

Cache::remember('users:list', 3600, function () {
    return User::all();
});

Step 11: Monitor Redis

CLI

redis-cli -a AVeryLongPassword... INFO stats
redis-cli -a AVeryLongPassword... INFO memory
redis-cli -a AVeryLongPassword... MONITOR  # ⚠️ live traffic

Live (top-like)

sudo apt install -y redis-tools
redis-cli -a AVeryLongPassword... --stat

With Grafana

Install redis_exporter and create a Redis dashboard. See the Prometheus + Grafana tutorial.

Step 12: Sentinel for high availability

If Redis becomes critical, set up Redis Sentinel (automatic failover) or Redis Cluster (sharding + HA). Out of scope here, but worth knowing for prod projects.

Troubleshooting

"OOM command not allowed when used memory > 'maxmemory'"

Redis hit its memory limit. Either increase maxmemory, or check maxmemory-policy = allkeys-lru (otherwise it refuses writes).

Connection refused

sudo systemctl status redis-server
sudo journalctl -u redis-server -n 50

Often an invalid config file issue after edit.

Persistence takes too long (Redis blocks)

On large databases (10+ GB), RDB snapshot can block. Solutions:

  • Migrate to AOF (appendonly yes + appendfsync everysec)
  • Disable persistence if pure cache
  • Use Redis Sentinel/Cluster for failover

Keys expire too fast

Check TTL:

TTL user:42
# 3599

No TTL? setex or expire not used. Or LRU eviction too aggressive (increase maxmemory).

Useful commands

# CLI connection
redis-cli -a PASSWORD

# Count keys
DBSIZE

# List keys (DEV only, slow)
KEYS *

# Scan keys (PROD)
SCAN 0 MATCH user:* COUNT 100

# Detailed info
INFO

# Memory stats
INFO memory

# Flush current DB (⚠️)
FLUSHDB

# View key TTL
TTL my_key

# Set with TTL
SETEX my_key 3600 "value"

# Slow query log
SLOWLOG GET 10

Conclusion

Redis is the most cost-effective tool to speed up an app. Benefits:

  • Response latency 10-100x lower on cached data
  • MySQL/PostgreSQL load reduced by 80-90%
  • Ability to handle 10x more traffic without adding resources

Going further:

  • Use Redis Streams for persistent queues
  • Set up Pub/Sub for inter-microservice communication
  • Migrate to KeyDB (Redis fork, multi-threaded, drop-in replacement)

Resources

Join our Discord community server

For any questions, suggestions, or just to chat with the community, join us on Discord!

900+Members