How to Install and Configure Redis Cache on Linux VPS: Complete Tutorial
Redis is the most widely used in-memory data store in the world — and for good reason. On a VPS running WordPress, Laravel, or any PHP application, Redis can reduce page load times by 60-90% and server CPU usage by 50-80% by caching database queries, user sessions, and computed data in RAM instead of hitting the disk-based MySQL database on every request.
This guide covers everything: installing Redis from scratch, securing it properly, configuring it for production performance, integrating it with PHP/WordPress/Laravel, monitoring its health, and diagnosing common problems. By the end, your VPS will have a fully operational Redis cache server that will dramatically change your site's performance profile.
Table of Contents
- What Is Redis and Why Use It?
- Prerequisites
- Installing Redis on Ubuntu/Debian
- Installing Redis on CentOS/RHEL/AlmaLinux
- Initial Redis Configuration
- Securing Redis (Critical)
- Persistence Configuration
- Memory Management and Eviction Policies
- Redis Data Structures and Use Cases
- Integrating Redis with PHP
- Integrating Redis with WordPress
- Integrating Redis with Laravel
- Integrating Redis with Nginx (FastCGI Cache + Redis Purge)
- Redis Cluster and Replication (Advanced)
- Monitoring and Performance Tuning
- Troubleshooting Common Problems
- Complete Configuration File Reference
- Performance Benchmark Results
- FAQ
- Conclusion
- Additional SEO Data
What Is Redis and Why Use It?
Redis (Remote Dictionary Server) is an open-source, in-memory data structure store. Unlike a traditional database that reads/writes to disk, Redis keeps all data in RAM — making operations typically 10,000× faster than a MySQL query.
Redis vs. Without Redis: Real Numbers
| Scenario | Without Redis | With Redis | Improvement |
|---|---|---|---|
| WordPress page load (uncached) | 800–2,000ms | 80–200ms | 10× faster |
| MySQL queries per page | 20–80 queries | 1–5 queries | 94% reduction |
| PHP-FPM CPU usage | 100% (under load) | 20–30% | 70% reduction |
| Concurrent users (2GB VPS) | 30–100 | 300–1,000 | 10× capacity |
| Server response time (TTFB) | 200–800ms | 20–80ms | 90% faster |
What Redis Caches
- Object cache: Database query results, WordPress transients, computed data
- Session storage: PHP sessions stored in Redis instead of disk files
- Full-page cache: Complete HTML responses (with proper PHP integration)
- Rate limiting: Track request counts per IP/user
- Queue storage: Background job queues (with Redis Lists)
- Pub/Sub messaging: Real-time notifications between processes
Redis vs. Memcached
| Feature | Redis | Memcached |
|---|---|---|
| Data structures | Strings, Lists, Sets, Hashes, Sorted Sets, Streams | Strings only |
| Persistence | Yes (RDB snapshots + AOF logs) | No (data lost on restart) |
| Replication | Master-replica + Cluster | No |
| Pub/Sub | Yes | No |
| Lua scripting | Yes | No |
| Memory efficiency | Slightly lower | Slightly higher |
| Use for | Production applications | Simple key-value cache only |
Verdict: Use Redis. Memcached's only advantage (slightly lower memory overhead) is rarely worth the missing features.
Prerequisites
# What you need before starting:
# - Ubuntu 20.04/22.04/24.04 LTS or CentOS/AlmaLinux 8/9
# - VPS with at least 1GB RAM (2GB+ recommended)
# - Root or sudo access
# - Basic familiarity with Linux command line
# Verify your Ubuntu/Debian version
lsb_release -a
# Verify your CentOS/AlmaLinux version
cat /etc/os-release
# Check available RAM
free -h
# Check current Redis installation (if any)
redis-server --version 2>/dev/null || echo "Redis not installed"
Installing Redis on Ubuntu/Debian
Method 1: Official Ubuntu Repository (Recommended for Most Users)
# Update package index
sudo apt update
# Install Redis
sudo apt install redis-server -y
# Verify installation
redis-server --version
# Expected: Redis server v=7.x.x ...
# Check service status
sudo systemctl status redis-server
# Should show: active (running)
# Enable Redis to start on boot
sudo systemctl enable redis-server
# Test basic connectivity
redis-cli ping
# Expected response: PONG
Method 2: Latest Redis from Official Redis Repository
The Ubuntu default repository may have an older Redis version. For the latest stable version:
# Add Redis official repository
curl -fsSL <https://packages.redis.io/gpg> | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] <https://packages.redis.io/deb> $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/redis.list
# Update and install
sudo apt update
sudo apt install redis -y
# Verify version (should be 7.x+)
redis-server --version
# Start and enable
sudo systemctl start redis-server
sudo systemctl enable redis-server
# Test
redis-cli ping
Method 3: Compile Redis from Source (Maximum Control)
# Install build dependencies
sudo apt install build-essential tcl pkg-config -y
# Download latest stable Redis
REDIS_VERSION="7.2.4"
wget <https://download.redis.io/releases/redis-${REDIS_VERSION}.tar.gz>
tar xzf redis-${REDIS_VERSION}.tar.gz
cd redis-${REDIS_VERSION}
# Compile with optimizations
make -j$(nproc)
# Run tests
make test
# Install to /usr/local/bin/
sudo make install
# Create Redis user and directories
sudo useradd --system --no-create-home --shell /bin/false redis
sudo mkdir -p /etc/redis /var/lib/redis /var/log/redis
sudo chown redis:redis /var/lib/redis /var/log/redis
# Copy default configuration
sudo cp redis.conf /etc/redis/redis.conf
sudo chown redis:redis /etc/redis/redis.conf
# Create systemd service
sudo tee /etc/systemd/system/redis.service << 'EOF'
[Unit]
Description=Redis In-Memory Data Store
After=network.target
[Service]
User=redis
Group=redis
ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf
ExecStop=/usr/local/bin/redis-cli shutdown
Restart=always
RestartSec=3
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl start redis
sudo systemctl enable redis
Installing Redis on CentOS/RHEL/AlmaLinux
# CentOS/AlmaLinux 8/9
# Enable EPEL repository
sudo dnf install epel-release -y
# Install Redis
sudo dnf install redis -y
# Or install from Remi repository (newer version)
sudo dnf install <https://rpms.remirepo.net/enterprise/remi-release-9.rpm> -y
sudo dnf module enable redis:remi-7.2 -y
sudo dnf install redis -y
# Start and enable
sudo systemctl start redis
sudo systemctl enable redis
# Verify
redis-server --version
redis-cli ping
# Allow Redis through firewall (if using firewalld)
# IMPORTANT: Only allow from localhost — never expose Redis to the internet!
sudo firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='127.0.0.1' port port='6379' protocol='tcp' accept"
sudo firewall-cmd --reload
Initial Redis Configuration
The default Redis configuration is not suitable for production. Here's how to configure it properly:
# Find your Redis config file location
redis-cli CONFIG GET dir
sudo find / -name redis.conf 2>/dev/null
# Usually: /etc/redis/redis.conf or /etc/redis.conf
# Open the configuration file
sudo nano /etc/redis/redis.conf
Core Configuration Settings
# /etc/redis/redis.conf — Core settings
# ============================================================
# NETWORK
# ============================================================
# Bind ONLY to localhost (CRITICAL security setting)
# Redis should NEVER be exposed to the internet
bind 127.0.0.1 ::1
# Use Unix socket for faster local connections (optional but faster)
unixsocket /var/run/redis/redis-server.sock
unixsocketperm 770
# Default port
port 6379
# Enable socket keepalive (detect dead clients)
tcp-keepalive 300
# ============================================================
# GENERAL
# ============================================================
# Run as daemon
daemonize yes
# PID file
pidfile /var/run/redis/redis-server.pid
# Log level: debug / verbose / notice / warning
loglevel notice
# Log file (empty = log to stdout)
logfile /var/log/redis/redis-server.log
# Number of databases (0-15, default 16)
databases 16
# ============================================================
# LIMITS
# ============================================================
# Maximum connected clients
maxclients 10000
# Maximum memory Redis will use
# IMPORTANT: Set this or Redis will use all available RAM
# Rule: set to 25-40% of total RAM for a shared server
# For 2GB VPS: 400-800mb
# For 4GB VPS: 1gb-1.6gb
maxmemory 512mb
# What to do when maxmemory is reached (eviction policy)
# For pure cache (no persistence needed): allkeys-lru
# For mixed use (some keys must not be evicted): volatile-lru
maxmemory-policy allkeys-lru
# ============================================================
# LAZY FREEING (Redis 4.0+)
# ============================================================
# Free memory asynchronously (prevents blocking during large DEL operations)
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
lazyfree-lazy-server-del yes
replica-lazy-flush yes
# Apply configuration changes
sudo systemctl restart redis-server
# Verify key settings applied
redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy
redis-cli CONFIG GET bind
# Check Redis is running on expected address/port
ss -tlnp | grep 6379
# Should show: 127.0.0.1:6379 LISTEN
# If you see 0.0.0.0:6379, Redis is exposed to ALL interfaces — INSECURE!
Securing Redis (Critical)
Redis has been used in many high-profile server breaches because of misconfiguration. A Redis server exposed to the internet without a password can be used to write SSH keys, execute cron jobs, and gain full server access.
1. Set a Strong Password
# Generate a strong password
openssl rand -base64 32
# Example output: K7mP2xQ9nR4sT1uV8wY3zA6bC5dE0fG
# Save this — you'll need it for your application configs
# Set the password in redis.conf
sudo nano /etc/redis/redis.conf
# /etc/redis/redis.conf
# Require authentication before any commands
requirepass "K7mP2xQ9nR4sT1uV8wY3zA6bC5dE0fG"
# ACL-based authentication (Redis 6.0+, more granular)
# user default on >yourpassword ~* &* +@all
# Disable dangerous commands
# Rename them to empty string to disable
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command DEBUG ""
rename-command CONFIG "CONFIG_9a8b7c6d" # rename instead of disable (needed by some apps)
# Restart Redis to apply
sudo systemctl restart redis-server
# Test authentication
redis-cli ping
# Expected: NOAUTH Authentication required.
redis-cli -a "K7mP2xQ9nR4sT1uV8wY3zA6bC5dE0fG" ping
# Expected: PONG
# Or authenticate after connecting
redis-cli
127.0.0.1:6379> AUTH "K7mP2xQ9nR4sT1uV8wY3zA6bC5dE0fG"
OK
127.0.0.1:6379> PING
PONG
2. Configure Linux Firewall
# Ubuntu/Debian with UFW
# Redis should ONLY be accessible from localhost
# Verify UFW is active
sudo ufw status
# Block Redis port from outside (just in case bind fails)
sudo ufw deny 6379
sudo ufw reload
# Verify no external access
nmap -p 6379 your.vps.ip.address
# Expected: 6379/tcp filtered redis
# CentOS/AlmaLinux with firewalld
sudo firewall-cmd --list-ports # Check what's open
# Redis port 6379 should NOT appear in public zone
3. Run Redis as Unprivileged User
# Verify Redis is NOT running as root
ps aux | grep redis
# Should show: redis 1234 0.0 0.4 ...
# NOT: root 1234 0.0 0.4 ...
# If running as root, fix in /etc/systemd/system/redis.service:
# User=redis
# Group=redis
# Check Redis file ownership
ls -la /etc/redis/redis.conf
# Should be: redis:redis, not root:root
4. Disable THP (Transparent Huge Pages) — Performance + Security
# THP causes Redis latency spikes and memory overhead
# Check current setting
cat /sys/kernel/mm/transparent_hugepage/enabled
# Shows: always [madvise] never
# 'always' = THP enabled (bad for Redis)
# Disable THP immediately
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/defrag
# Make permanent across reboots
sudo tee /etc/rc.local << 'EOF'
#!/bin/bash
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
EOF
sudo chmod +x /etc/rc.local
# Or via systemd (more reliable)
sudo tee /etc/systemd/system/disable-thp.service << 'EOF'
[Unit]
Description=Disable Transparent Huge Pages
DefaultDependencies=no
After=sysinit.target local-fs.target
Before=redis-server.service
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/enabled && echo never > /sys/kernel/mm/transparent_hugepage/defrag'
[Install]
WantedBy=basic.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable disable-thp
sudo systemctl start disable-thp
5. Configure Kernel Parameters
# /etc/sysctl.conf — Kernel settings for optimal Redis performance
sudo tee -a /etc/sysctl.conf << 'EOF'
# Redis: Allow overcommit memory (prevents background save failures)
vm.overcommit_memory = 1
# Redis: Reduce swappiness (keep Redis data in RAM)
vm.swappiness = 10
# Network: increase TCP backlog
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
EOF
# Apply immediately
sudo sysctl -p
# Verify
sysctl vm.overcommit_memory
# Expected: vm.overcommit_memory = 1
Persistence Configuration
Redis offers two persistence mechanisms. Understanding both helps you choose the right trade-off between performance and data safety.
RDB (Redis Database Snapshots)
RDB periodically saves a snapshot of all data to disk.
# /etc/redis/redis.conf — RDB settings
# Save snapshot if N keys changed within M seconds
# Format: save <seconds> <changes>
save 900 1 # Save after 900s if at least 1 key changed
save 300 10 # Save after 300s if at least 10 keys changed
save 60 10000 # Save after 60s if at least 10000 keys changed
# For pure cache (data loss acceptable, maximum performance):
# Comment out all save lines or:
# save ""
# RDB file name and location
dbfilename dump.rdb
dir /var/lib/redis
# Compress RDB files (saves disk space, uses slightly more CPU)
rdbcompression yes
rdbchecksum yes
# Stop accepting writes if RDB save fails (data integrity)
stop-writes-on-bgsave-error yes
AOF (Append Only File)
AOF logs every write operation for point-in-time recovery.
# /etc/redis/redis.conf — AOF settings
# Enable AOF
appendonly yes
# AOF filename
appendfilename "appendonly.aof"
# Fsync policy:
# always: sync every write (safest, slowest — significant performance impact)
# everysec: sync every second (good balance — max 1 second data loss)
# no: let OS handle sync (fastest, most data loss risk)
appendfsync everysec
# Don't fsync during background save (prevents latency spikes)
no-appendfsync-on-rewrite yes
# Automatically rewrite AOF file when it grows too large
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
Recommended Persistence Strategy by Use Case
# CASE 1: Pure cache (WordPress object cache)
# Data loss = acceptable (cache can be rebuilt)
# Best setting: No persistence (fastest performance)
# save ""
# appendonly no
# CASE 2: Session storage
# Data loss = causes user logouts (annoying but not critical)
# Best setting: RDB with moderate intervals
# save 300 10
# appendonly no
# CASE 3: Queue / job data
# Data loss = lost jobs (critical)
# Best setting: AOF with everysec
# appendonly yes
# appendfsync everysec
# CASE 4: Primary data store
# Data loss = catastrophic
# Best setting: Both RDB + AOF
# save 900 1
# save 300 10
# appendonly yes
# appendfsync always (accept performance penalty)
Memory Management and Eviction Policies
Understanding Eviction Policies
# When Redis reaches maxmemory, it uses the eviction policy to free space
# Available policies:
# noeviction — Return error when memory is full (DO NOT USE for cache)
maxmemory-policy noeviction
# allkeys-lru — Evict least recently used keys (BEST for pure cache)
maxmemory-policy allkeys-lru
# volatile-lru — Evict LRU keys with TTL set (for mixed use: cache + permanent data)
maxmemory-policy volatile-lru
# allkeys-lfu — Evict least frequently used keys (Redis 4.0+, good for "hot" data)
maxmemory-policy allkeys-lfu
# volatile-lfu — LFU only for keys with TTL
maxmemory-policy volatile-lfu
# allkeys-random — Evict random keys
maxmemory-policy allkeys-random
# volatile-random — Evict random keys with TTL
maxmemory-policy volatile-random
# volatile-ttl — Evict keys with shortest TTL first
maxmemory-policy volatile-ttl
Memory Sizing Guide
# Check current Redis memory usage
redis-cli -a "yourpassword" INFO memory
# Key metrics:
# used_memory_human: current memory usage
# used_memory_peak_human: peak memory usage
# mem_fragmentation_ratio: should be 1.0-1.5 (>1.5 = fragmentation issue)
# maxmemory_human: configured limit
# Calculate right maxmemory for your VPS:
# Rule: Allocate 20-40% of total RAM to Redis
# Leave room for: OS (300-500MB), PHP-FPM, MySQL, Nginx
# Example for 2GB VPS:
# OS: ~400MB
# Nginx: ~50MB
# PHP-FPM (10 workers × 40MB): ~400MB
# MySQL: ~600MB
# Redis: ~400MB (remaining)
# Total: ~1850MB
# Example for 4GB VPS:
# OS: ~400MB
# Nginx: ~50MB
# PHP-FPM (20 workers × 40MB): ~800MB
# MySQL: ~1200MB
# Redis: ~1000MB
# Total: ~3450MB
# Monitor Redis memory over time
watch -n 1 'redis-cli -a "yourpassword" INFO memory | grep -E "used_memory_human|maxmemory_human|mem_fragmentation"'
LFU Configuration (Redis 4.0+)
# When using allkeys-lfu or volatile-lfu policy
# LFU counter decay period (higher = slower decay = remembers more history)
lfu-decay-time 1
# LFU counter initial value for new keys
# Higher = new keys are less likely to be evicted immediately
lfu-log-factor 10
Redis Data Structures and Use Cases
Understanding Redis data types helps you use it effectively beyond simple key-value storage.
# Connect to Redis CLI for examples
redis-cli -a "yourpassword"
# ============================================================
# STRINGS — Simple key-value pairs
# ============================================================
SET user:1:name "Alice" # Set key with value
GET user:1:name # Get value
SETEX session:abc123 3600 "user_data" # Set with expiry (3600 seconds)
INCR page_views # Atomic increment (for counters)
INCRBY page_views 5 # Increment by N
TTL session:abc123 # Check remaining TTL
DEL user:1:name # Delete key
# ============================================================
# HASHES — Object-like storage
# ============================================================
HSET user:1 name "Alice" email "alice@example.com" age 30
HGET user:1 name # Get single field
HGETALL user:1 # Get all fields
HMSET user:2 name "Bob" email "bob@example.com"
HDEL user:1 age # Delete field
HEXISTS user:1 email # Check if field exists
# ============================================================
# LISTS — Ordered, allows duplicates
# ============================================================
RPUSH jobs "send_email" "resize_image" "update_sitemap" # Append to list
LPUSH jobs "urgent_task" # Prepend to list
LRANGE jobs 0 -1 # Get all items
LPOP jobs # Pop from left (queue dequeue)
RPOP jobs # Pop from right (stack pop)
LLEN jobs # List length
BLPOP jobs 30 # Blocking pop (wait up to 30s)
# ============================================================
# SETS — Unordered, unique values
# ============================================================
SADD online_users "user:1" "user:2" "user:3"
SMEMBERS online_users # Get all members
SISMEMBER online_users "user:1" # Check membership
SREM online_users "user:1" # Remove member
SCARD online_users # Count members
SINTER admin_users online_users # Intersection
SUNION online_users vip_users # Union
# ============================================================
# SORTED SETS — Ordered by score
# ============================================================
ZADD leaderboard 1500 "player:alice" # Add with score
ZADD leaderboard 2300 "player:bob"
ZADD leaderboard 1800 "player:carol"
ZRANGE leaderboard 0 -1 WITHSCORES # Get all (ascending)
ZREVRANGE leaderboard 0 2 WITHSCORES # Top 3 players (descending)
ZRANK leaderboard "player:alice" # Get rank (0-based)
ZSCORE leaderboard "player:bob" # Get score
ZINCRBY leaderboard 100 "player:alice" # Increment score
# ============================================================
# PUB/SUB — Real-time messaging
# ============================================================
# Terminal 1 (subscriber):
SUBSCRIBE notifications
# Terminal 2 (publisher):
PUBLISH notifications "New order placed: #12345"
# Terminal 1 receives:
# 1) "message"
# 2) "notifications"
# 3) "New order placed: #12345"
Integrating Redis with PHP
Install PHP Redis Extension
# Method 1: From package manager (easiest)
sudo apt install php-redis -y
# Or for specific PHP version:
sudo apt install php8.2-redis -y
# Method 2: Install via PECL
sudo apt install php-pear php-dev -y
sudo pecl install redis
# Enable the extension
echo "extension=redis.so" | sudo tee /etc/php/8.2/mods-available/redis.ini
sudo phpenmod redis
# Verify extension is loaded
php -m | grep redis
# Expected: redis
# Restart PHP-FPM
sudo systemctl restart php8.2-fpm
# Check in PHP info
php -r "echo phpversion('redis');"
# Expected: 6.x.x
PHP Redis Connection Examples
<?php
// ============================================================
// BASIC CONNECTION
// ============================================================
$redis = new Redis();
// TCP connection (default)
$redis->connect('127.0.0.1', 6379);
// Unix socket connection (faster, recommended for same-server)
$redis->connect('/var/run/redis/redis-server.sock');
// Authenticate if password is set
$redis->auth('K7mP2xQ9nR4sT1uV8wY3zA6bC5dE0fG');
// Select database (0-15)
$redis->select(0);
// Test connection
echo $redis->ping(); // PONG
// ============================================================
// PERSISTENT CONNECTION (reuses connection across requests)
// ============================================================
$redis = new Redis();
$redis->pconnect('127.0.0.1', 6379, 2.5); // 2.5 second timeout
$redis->auth('yourpassword');
// ============================================================
// CACHING PATTERN: Cache-Aside
// ============================================================
class RedisCache {
private Redis $redis;
private int $defaultTtl;
public function __construct(string $password = '', int $defaultTtl = 3600) {
$this->redis = new Redis();
$this->redis->pconnect('/var/run/redis/redis-server.sock');
if ($password) {
$this->redis->auth($password);
}
$this->defaultTtl = $defaultTtl;
}
public function remember(string $key, callable $callback, int $ttl = null): mixed {
$ttl ??= $this->defaultTtl;
// Try to get from cache
$cached = $this->redis->get($key);
if ($cached !== false) {
return json_decode($cached, true);
}
// Cache miss: execute callback and store result
$value = $callback();
$this->redis->setex($key, $ttl, json_encode($value));
return $value;
}
public function forget(string $key): bool {
return (bool) $this->redis->del($key);
}
public function flush(): bool {
return $this->redis->flushDB();
}
public function increment(string $key, int $by = 1): int {
return $this->redis->incrBy($key, $by);
}
}
// Usage:
$cache = new RedisCache('yourpassword', 3600);
// Cache a database query for 30 minutes
$products = $cache->remember('products:page1', function() use ($db) {
return $db->query("SELECT * FROM products WHERE active = 1 ORDER BY created_at DESC LIMIT 20");
}, 1800);
// Cache with automatic key generation
$user = $cache->remember("user:{$userId}", function() use ($db, $userId) {
return $db->query("SELECT * FROM users WHERE id = ?", [$userId]);
});
// Invalidate cache when data changes
$cache->forget('products:page1');
$cache->forget("user:{$userId}");
// ============================================================
// RATE LIMITING WITH REDIS
// ============================================================
function isRateLimited(Redis $redis, string $identifier, int $limit = 100, int $window = 3600): bool {
$key = "rate_limit:{$identifier}";
$current = $redis->incr($key);
if ($current === 1) {
// First request in window — set expiry
$redis->expire($key, $window);
}
return $current > $limit;
}
// Usage:
$clientIp = $_SERVER['REMOTE_ADDR'];
if (isRateLimited($redis, $clientIp, 100, 3600)) {
http_response_code(429);
die('Too many requests. Please try again later.');
}
// ============================================================
// SESSION STORAGE IN REDIS
// ============================================================
// Store PHP sessions in Redis instead of disk
// Much faster and works across multiple servers
// Option 1: php.ini configuration
// session.save_handler = redis
// session.save_path = "tcp://127.0.0.1:6379?auth=yourpassword"
// Option 2: In your PHP code
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://127.0.0.1:6379?auth=yourpassword&database=1');
session_start();
// Or via Unix socket:
ini_set('session.save_path', 'unix:///var/run/redis/redis-server.sock?auth=yourpassword&database=1');
// ============================================================
// DISTRIBUTED LOCKING (Prevent Race Conditions)
// ============================================================
function acquireLock(Redis $redis, string $resource, int $ttl = 30): ?string {
$token = bin2hex(random_bytes(16));
$key = "lock:{$resource}";
// SET key value NX EX ttl (atomic: only set if key doesn't exist)
if ($redis->set($key, $token, ['NX', 'EX' => $ttl])) {
return $token;
}
return null; // Lock not acquired
}
function releaseLock(Redis $redis, string $resource, string $token): bool {
$key = "lock:{$resource}";
// Use Lua script for atomic check-and-delete
$lua = <<<'EOT'
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
else
return 0
end
EOT;
return (bool) $redis->eval($lua, [$key, $token], 1);
}
// Usage: prevent duplicate payment processing
$lockToken = acquireLock($redis, 'payment:' . $orderId);
if (!$lockToken) {
die('Payment already being processed');
}
try {
// Process payment
processPayment($orderId);
} finally {
releaseLock($redis, 'payment:' . $orderId, $lockToken);
}
Integrating Redis with WordPress
Install Redis Object Cache Plugin
# Method 1: Via WP-CLI (recommended)
wp --path=/var/www/yourdomain.com/public_html plugin install redis-cache --activate
# Enable Redis object cache
wp --path=/var/www/yourdomain.com/public_html redis enable
# Check status
wp --path=/var/www/yourdomain.com/public_html redis status
# Expected: Status: Connected
# Method 2: Manual installation via WordPress admin
# Plugins > Add New > Search 'Redis Object Cache' > Install > Activate
# Settings > Redis > Enable Object Cache
Configure WordPress for Redis
// wp-config.php — Add BEFORE the "That's all, stop editing!" line
// Redis host and port
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);
// Or use Unix socket (faster)
define('WP_REDIS_SCHEME', 'unix');
define('WP_REDIS_PATH', '/var/run/redis/redis-server.sock');
// Authentication
define('WP_REDIS_PASSWORD', 'K7mP2xQ9nR4sT1uV8wY3zA6bC5dE0fG');
// Use database 0 for cache (keep separate databases for different sites)
define('WP_REDIS_DATABASE', 0);
// Prefix (IMPORTANT for multiple WordPress sites on same Redis)
define('WP_REDIS_PREFIX', 'mysite_');
// Cache group exclusions (don't cache these)
define('WP_REDIS_IGNORED_GROUPS', ['counts', 'plugins']);
// Connection timeout
define('WP_REDIS_TIMEOUT', 1);
define('WP_REDIS_READ_TIMEOUT', 1);
// Fail gracefully (use MySQL cache if Redis is unavailable)
define('WP_REDIS_GRACEFUL', true);
// Maximum time-to-live (seconds)
define('WP_REDIS_MAXTTL', 86400); // 24 hours
Verify WordPress Redis Integration
# Check that Redis is receiving WordPress cache data
redis-cli -a "yourpassword" INFO keyspace
# Expected: db0:keys=1234,expires=1200,avg_ttl=...
# Monitor Redis commands in real-time
redis-cli -a "yourpassword" MONITOR
# Should see: GET, SET commands from WordPress
# (Press Ctrl+C to stop)
# Check cache hit statistics
redis-cli -a "yourpassword" INFO stats | grep -E 'keyspace_hits|keyspace_misses'
# Calculate hit rate: hits / (hits + misses) × 100
# Good hit rate: > 90%
# WP-CLI debug
wp --path=/var/www/yourdomain.com/public_html redis status
wp --path=/var/www/yourdomain.com/public_html redis diagnostics
# Check object cache file exists
ls -la /var/www/yourdomain.com/public_html/wp-content/object-cache.php
# This file must exist for Redis cache to be active
WordPress-Specific Redis Optimization
// functions.php — Advanced Redis optimization for WordPress
// Increase cache TTL for specific WordPress data types
add_filter('redis_cache_expiry', function($expiry, $group) {
$long_lived_groups = ['terms', 'posts', 'options', 'site-options'];
if (in_array($group, $long_lived_groups)) {
return 86400; // 24 hours for stable data
}
return $expiry; // Default for everything else
}, 10, 2);
// Pre-warm cache for critical data on publishing
add_action('save_post', function($post_id) {
if (wp_is_post_revision($post_id)) return;
// Flush related cache keys
wp_cache_delete('post_' . $post_id, 'posts');
wp_cache_delete('post_meta_' . $post_id, 'post_meta');
// Re-populate cache
get_post($post_id);
});
// Store WordPress transients in Redis with proper TTL
// WordPress already handles this automatically with Redis Object Cache plugin
// But you can set custom TTL:
set_transient('expensive_query_result', $result, HOUR_IN_SECONDS);
$cached = get_transient('expensive_query_result');
Integrating Redis with Laravel
# Install Predis (PHP Redis client) or use phpredis extension
composer require predis/predis
# Or use phpredis extension (faster, C extension)
# sudo apt install php8.2-redis -y
# In config/database.php, change client to 'phpredis'
// .env
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=K7mP2xQ9nR4sT1uV8wY3zA6bC5dE0fG
REDIS_PORT=6379
REDIS_DB=0
# Use different databases for different cache stores
REDIS_CACHE_DB=1
REDIS_QUEUE_DB=2
REDIS_SESSION_DB=3
// config/database.php
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
],
'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_CACHE_DB', '1'),
],
],
// config/cache.php
'default' => env('CACHE_DRIVER', 'redis'),
'stores' => [
'redis' => [
'driver' => 'redis',
'connection' => 'cache',
'lock_connection' => 'default',
],
],
// config/session.php
'driver' => env('SESSION_DRIVER', 'redis'),
'connection' => 'default',
// Laravel usage examples
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;
// Basic cache operations
Cache::put('key', 'value', now()->addHours(1));
$value = Cache::get('key', 'default');
Cache::forget('key');
// Remember pattern (most common)
$products = Cache::remember('products:featured', 3600, function () {
return Product::where('featured', true)->with('images')->get();
});
// Cache tags (group related items for easy invalidation)
$posts = Cache::tags(['posts', 'user:' . $userId])->remember('user_posts', 3600, function () use ($userId) {
return Post::where('user_id', $userId)->get();
});
// Invalidate all cache for a user
Cache::tags('user:' . $userId)->flush();
// Atomic locks
$lock = Cache::lock('payment:' . $orderId, 30);
if ($lock->get()) {
try {
processPayment($orderId);
} finally {
$lock->release();
}
}
// Direct Redis operations
Redis::set('counter', 0);
Redis::incr('counter');
$count = Redis::get('counter');
// Laravel Queues with Redis
// .env: QUEUE_CONNECTION=redis
// Dispatch job: ProcessOrder::dispatch($order)
// Run worker: php artisan queue:work redis --sleep=3 --tries=3
Integrating Redis with Nginx (FastCGI Cache + Redis Purge)
Combining Nginx FastCGI cache with Redis-based cache purging gives you the best of both worlds: the speed of Nginx's in-memory page cache with Redis-powered intelligent invalidation.
# Install Nginx with cache purge module
sudo apt install nginx-extras -y # Includes ngx_cache_purge module
# Or compile with purge module:
# nginx -V 2>&1 | grep ngx_cache_purge
# If missing, recompile or use nginx-extras package
# /etc/nginx/nginx.conf (http block)
fastcgi_cache_path /var/cache/nginx
levels=1:2
keys_zone=WORDPRESS:100m
inactive=60m
max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_cache_lock on;
# /etc/nginx/sites-available/yourdomain.com
server {
listen 443 ssl http2;
server_name yourdomain.com;
root /var/www/yourdomain.com/public_html;
set $skip_cache 0;
# Skip cache conditions
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin/|/wp-login|/cart|/checkout") { set $skip_cache 1; }
if ($http_cookie ~* "wordpress_logged_in|woocommerce_cart_hash") { set $skip_cache 1; }
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 301 302 1h;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-FastCGI-Cache $upstream_cache_status;
add_header X-Cache-Status $upstream_cache_status;
}
# Cache purge endpoint
location ~ /purge(/.*) {
fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
allow 127.0.0.1;
deny all;
}
}
// Purge Nginx cache when WordPress publishes/updates content
// Add to functions.php or a custom plugin
function purge_nginx_cache_for_url(string $url): bool {
$purge_url = str_replace(
parse_url($url, PHP_URL_HOST),
'127.0.0.1',
$url . '/*'
);
$response = wp_remote_request($purge_url, [
'method' => 'PURGE',
'headers' => ['Host' => parse_url($url, PHP_URL_HOST)],
]);
return !is_wp_error($response);
}
// Auto-purge on post save
add_action('save_post', function($post_id) {
if (wp_is_post_revision($post_id)) return;
$post_url = get_permalink($post_id);
purge_nginx_cache_for_url($post_url);
purge_nginx_cache_for_url(home_url('/')); // Also purge homepage
});
// Auto-purge on comment post
add_action('comment_post', function($comment_id) {
$comment = get_comment($comment_id);
$post_url = get_permalink($comment->comment_post_ID);
purge_nginx_cache_for_url($post_url);
});
Redis Cluster and Replication (Advanced)
Master-Replica Replication
# Setup: 1 master (writes) + 1 replica (reads)
# Master: 192.168.1.10
# Replica: 192.168.1.11
# On REPLICA server — /etc/redis/redis.conf:
replicaof 192.168.1.10 6379
masterauth "masterpassword"
replica-read-only yes
replica-serve-stale-data yes
# Restart replica
sudo systemctl restart redis
# Verify replication on master
redis-cli -a "masterpassword" INFO replication
# Expected:
# role: master
# connected_slaves: 1
# slave0: ip=192.168.1.11, port=6379, state=online, offset=...
# Verify replication on replica
redis-cli -a "masterpassword" -h 192.168.1.11 INFO replication
# Expected:
# role: slave
# master_host: 192.168.1.10
# master_link_status: up
Redis Sentinel (Automatic Failover)
# Sentinel monitors masters and promotes replicas automatically if master fails
# Requires minimum 3 Sentinel nodes for quorum
# Install Sentinel
sudo apt install redis-sentinel -y
# /etc/redis/sentinel.conf:
sentinel monitor mymaster 192.168.1.10 6379 2
sentinel auth-pass mymaster "masterpassword"
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
# Start Sentinel
sudo systemctl start redis-sentinel
sudo systemctl enable redis-sentinel
# Monitor Sentinel status
redis-cli -p 26379 SENTINEL masters
redis-cli -p 26379 SENTINEL replicas mymaster
Monitoring and Performance Tuning
Essential Redis Monitoring Commands
# Full server info
redis-cli -a "yourpassword" INFO all
# Key sections to monitor:
redis-cli -a "yourpassword" INFO server # Server info
redis-cli -a "yourpassword" INFO clients # Connected clients
redis-cli -a "yourpassword" INFO memory # Memory usage
redis-cli -a "yourpassword" INFO stats # Operations statistics
redis-cli -a "yourpassword" INFO replication # Replication status
redis-cli -a "yourpassword" INFO keyspace # Database key counts
# Real-time statistics
redis-cli -a "yourpassword" --stat
# Shows: keys, mem, clients, blocked, requests per second
# Command statistics (most expensive operations)
redis-cli -a "yourpassword" INFO commandstats | sort -t= -k2 -rn | head -20
# Slow log (queries taking longer than X microseconds)
redis-cli -a "yourpassword" CONFIG SET slowlog-log-slower-than 10000 # 10ms
redis-cli -a "yourpassword" SLOWLOG GET 10 # Show 10 slowest commands
redis-cli -a "yourpassword" SLOWLOG RESET # Clear slow log
# Memory analysis
redis-cli -a "yourpassword" MEMORY DOCTOR # AI-powered diagnosis
redis-cli -a "yourpassword" MEMORY STATS # Detailed memory breakdown
# Find large keys
redis-cli -a "yourpassword" --bigkeys
# Scans entire database and reports largest keys per type
# Live command monitoring (DEBUG)
redis-cli -a "yourpassword" MONITOR
# WARNING: Very high overhead — use briefly for debugging only!
# Latency monitoring
redis-cli -a "yourpassword" --latency # Continuous latency monitoring
redis-cli -a "yourpassword" --latency-history # Historical latency
redis-cli -a "yourpassword" --latency-dist # Latency distribution
Calculate Cache Hit Rate
# Get hit/miss statistics
redis-cli -a "yourpassword" INFO stats | grep -E 'keyspace_hits|keyspace_misses'
# Example output:
# keyspace_hits: 1523847
# keyspace_misses: 45231
# Calculate hit rate:
# hit_rate = hits / (hits + misses) × 100
# 1523847 / (1523847 + 45231) × 100 = 97.1%
# Target hit rates:
# WordPress object cache: > 90%
# Session cache: > 99% (sessions should always hit)
# Full-page cache: > 70% (many pages bypass due to cookies)
# Script to calculate and alert on low hit rate:
#!/bin/bash
HITS=$(redis-cli -a "pass" INFO stats | grep keyspace_hits | awk -F: '{print $2}' | tr -d '\r')
MISSES=$(redis-cli -a "pass" INFO stats | grep keyspace_misses | awk -F: '{print $2}' | tr -d '\r')
TOTAL=$((HITS + MISSES))
if [ $TOTAL -gt 0 ]; then
RATE=$((HITS * 100 / TOTAL))
echo "Cache hit rate: ${RATE}%"
if [ $RATE -lt 80 ]; then
echo "WARNING: Cache hit rate is low (${RATE}%). Consider increasing maxmemory."
fi
fi
Automated Monitoring Script
#!/bin/bash
# /usr/local/bin/redis-health-check.sh
# Run via cron: */5 * * * * /usr/local/bin/redis-health-check.sh
REDIS_PASSWORD="yourpassword"
ALERT_EMAIL="admin@yourdomain.com"
HOSTNAME=$(hostname)
# Check if Redis is responding
if ! redis-cli -a "$REDIS_PASSWORD" ping > /dev/null 2>&1; then
echo "Redis DOWN on $HOSTNAME" | mail -s "[CRITICAL] Redis Down" $ALERT_EMAIL
sudo systemctl restart redis-server # Auto-restart
exit 1
fi
# Check memory usage
USED_MB=$(redis-cli -a "$REDIS_PASSWORD" INFO memory | grep 'used_memory:' | awk -F: '{print $2}' | tr -d '\r')
MAX_MB=$(redis-cli -a "$REDIS_PASSWORD" CONFIG GET maxmemory | tail -1)
MAX_MB=${MAX_MB:-1073741824} # Default 1GB if not set
USED_PCT=$((USED_MB * 100 / MAX_MB))
if [ $USED_PCT -gt 90 ]; then
echo "Redis memory usage is ${USED_PCT}% on $HOSTNAME" | mail -s "[WARNING] Redis High Memory" $ALERT_EMAIL
fi
# Check connected clients
CLIENTS=$(redis-cli -a "$REDIS_PASSWORD" INFO clients | grep 'connected_clients:' | awk -F: '{print $2}' | tr -d '\r')
if [ "$CLIENTS" -gt 500 ]; then
echo "Redis has $CLIENTS connected clients on $HOSTNAME" | mail -s "[WARNING] Redis High Connections" $ALERT_EMAIL
fi
echo "Redis health check OK. Memory: ${USED_PCT}%, Clients: $CLIENTS"
Netdata Redis Integration
# Netdata automatically detects Redis if installed
# Dashboard shows: ops/sec, hit rate, memory, connected clients
# Verify Netdata is monitoring Redis
cat /etc/netdata/python.d/redis.conf
# Or:
cat /etc/netdata/go.d/redis.conf
# Typical Redis dashboard shows:
# - Operations per second (GET, SET, DEL)
# - Cache hit rate over time
# - Memory usage and fragmentation
# - Connected clients
# - Evicted keys per second
# - Expired keys per second
Troubleshooting Common Problems
Problem 1: Redis Won't Start
# Check error log
sudo journalctl -u redis-server -n 50 --no-pager
sudo tail -50 /var/log/redis/redis-server.log
# Common causes:
# 1. Port already in use
sudo ss -tlnp | grep 6379
# Kill existing process or change port in redis.conf
# 2. Permission issues
ls -la /var/lib/redis/
# Should be: drwxr-x--- 2 redis redis
ls -la /var/log/redis/
# Should be: drwxr-xr-x 2 redis redis
# Fix permissions:
sudo chown -R redis:redis /var/lib/redis /var/log/redis /etc/redis
# 3. Invalid configuration
redis-server /etc/redis/redis.conf --test
# Shows configuration errors
# 4. Kernel parameter issues
check: dmesg | tail -20
# May show: WARNING overcommit_memory is set to 0
# Fix: echo 1 | sudo tee /proc/sys/vm/overcommit_memory
Problem 2: High Memory Usage / OOM
# Check what's using memory
redis-cli -a "pass" MEMORY DOCTOR
redis-cli -a "pass" --bigkeys
# Check if eviction is working
redis-cli -a "pass" INFO stats | grep evicted_keys
# If evicted_keys keeps growing rapidly = maxmemory too small
# Check fragmentation ratio
redis-cli -a "pass" INFO memory | grep mem_fragmentation_ratio
# >1.5 = high fragmentation
# Fix: restart Redis (defragments memory)
# Or: redis-cli -a "pass" MEMORY PURGE (Redis 4.0+)
# Find and delete large key groups
redis-cli -a "pass" --scan --pattern 'session:*' | wc -l
# If sessions are growing, check session cleanup code
# Delete all keys matching pattern (CAREFUL in production!)
redis-cli -a "pass" --scan --pattern 'cache:old:*' | xargs redis-cli -a "pass" DEL
# Check if swap is being used
cat /proc/$(pgrep redis)/status | grep -i vmswap
# VmSwap: 0 kB is good (no swap)
# VmSwap: large number = performance degradation
Problem 3: Slow Response Times
# Check slow log
redis-cli -a "pass" SLOWLOG GET 20
# Look for: commands taking > 10ms
# Common causes of slow Redis:
# 1. Large key operations (HGETALL on hash with 100k fields)
# Solution: Redesign data model, use HSCAN instead
# 2. Expensive commands: KEYS * (NEVER use in production)
# Solution: Replace KEYS * with SCAN
redis-cli -a "pass" SCAN 0 MATCH 'cache:*' COUNT 100
# SCAN is safe for production, KEYS * blocks all operations!
# 3. Memory fragmentation causing GC pressure
redis-cli -a "pass" DEBUG JMAP # Memory allocation report
# 4. THP (Transparent Huge Pages) enabled
cat /sys/kernel/mm/transparent_hugepage/enabled
# Should be: [never] not [always]
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
# 5. Too many clients creating connections
# Use connection pooling instead of new connection per request
# PHP: use pconnect() instead of connect()
# Measure actual latency
redis-cli -a "pass" --intrinsic-latency 60 # 60-second test
# Acceptable: < 1ms for 99th percentile on SSD VPS
Problem 4: Authentication Fails
# Test authentication
redis-cli -a "yourpassword" ping
# Check if password is set correctly
grep requirepass /etc/redis/redis.conf
# Test via PHP
php -r "
\$r = new Redis();
\$r->connect('127.0.0.1', 6379);
\$result = \$r->auth('yourpassword');
var_dump(\$result); // bool(true)
"
# Special characters in password can cause issues
# Escape in redis.conf: requirepass 'P@ssw0rd!#'
# Use single quotes for passwords with special characters
Problem 5: WordPress Redis Cache Not Working
# Verify object-cache.php exists
ls -la /var/www/yourdomain.com/public_html/wp-content/object-cache.php
# Check PHP Redis extension is loaded
php -m | grep -i redis
# Test Redis connection from PHP context
php -r "
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);
define('WP_REDIS_PASSWORD', 'yourpassword');
\$redis = new Redis();
\$redis->connect(WP_REDIS_HOST, WP_REDIS_PORT);
\$redis->auth(WP_REDIS_PASSWORD);
echo 'Connected: ' . \$redis->ping() . PHP_EOL;
"
# Check WordPress Redis plugin diagnostics
wp --path=/var/www/yourdomain.com/public_html redis diagnostics
# Verify Redis is receiving WordPress data
watch -n 2 'redis-cli -a "yourpassword" INFO keyspace'
# Should show increasing keys in db0
# PHP-FPM user must have access to Redis socket
# If using Unix socket:
ls -la /var/run/redis/redis-server.sock
# Must be readable by www-data (PHP-FPM user)
sudo usermod -a -G redis www-data
sudo systemctl restart php8.2-fpm
Complete Configuration File Reference
# /etc/redis/redis.conf — Production-ready complete configuration
# For: Ubuntu 22.04 VPS, 2-4GB RAM, WordPress/PHP application
# Redis 7.x
# ============================================================
# NETWORK
# ============================================================
bind 127.0.0.1 ::1
protected-mode yes
port 6379
tcp-backlog 511
tcp-keepalive 300
# Unix socket (faster than TCP for local connections)
unixsocket /var/run/redis/redis-server.sock
unixsocketperm 770
# ============================================================
# GENERAL
# ============================================================
daemonize yes
pidfile /var/run/redis/redis-server.pid
loglevel notice
logfile /var/log/redis/redis-server.log
databases 16
always-show-logo no
# ============================================================
# SECURITY
# ============================================================
requirepass "CHANGE-THIS-TO-YOUR-STRONG-PASSWORD"
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command DEBUG ""
rename-command KEYS ""
# ============================================================
# MEMORY MANAGEMENT
# ============================================================
# Set to 25-40% of total RAM
maxmemory 512mb
maxmemory-policy allkeys-lru
maxmemory-samples 5
# Lazy freeing (non-blocking DEL/FLUSH)
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
lazyfree-lazy-server-del yes
replica-lazy-flush yes
# ============================================================
# PERSISTENCE
# ============================================================
# For pure cache: disable all persistence
# Comment out save lines:
# save 900 1
# save 300 10
# save 60 10000
# For cache + sessions: enable RDB only:
save 900 1
save 300 10
dbfilename dump.rdb
dir /var/lib/redis
rdbcompression yes
rdbchecksum yes
stop-writes-on-bgsave-error yes
# AOF: Enable for critical data (queues, important keys)
# appendonly yes
# appendfilename "appendonly.aof"
# appendfsync everysec
# no-appendfsync-on-rewrite yes
# auto-aof-rewrite-percentage 100
# auto-aof-rewrite-min-size 64mb
# ============================================================
# PERFORMANCE
# ============================================================
tcp-backlog 511
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
# ============================================================
# LIMITS
# ============================================================
maxclients 10000
# ============================================================
# SLOW LOG
# ============================================================
slowlog-log-slower-than 10000
slowlog-max-len 128
# ============================================================
# LATENCY MONITORING
# ============================================================
latency-monitor-threshold 100
latency-tracking yes
# ============================================================
# ADVANCED SETTINGS
# ============================================================
hash-max-listpack-entries 128
hash-max-listpack-value 64
list-max-listpack-size -2
set-max-intset-entries 512
zset-max-listpack-entries 128
zset-max-listpack-value 64
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
Performance Benchmark Results
Here are typical benchmark results on a 2-core/2GB VPS (DigitalOcean Droplet) running Ubuntu 22.04:
# Run benchmarks yourself
redis-benchmark -a "yourpassword" -q -n 100000 -c 50
# Typical results on 2GB VPS (SSD):
# ====================================================
# SET: 85,000 ops/sec
# GET: 95,000 ops/sec
# INCR: 90,000 ops/sec
# LPUSH: 85,000 ops/sec
# RPUSH: 83,000 ops/sec
# SADD: 88,000 ops/sec
# ZADD: 80,000 ops/sec
# HSET: 84,000 ops/sec
# ====================================================
# With Unix socket (faster than TCP):
redis-benchmark -a "yourpassword" -q -n 100000 -c 50 -s /var/run/redis/redis-server.sock
# GET: 110,000 ops/sec (~15-20% faster than TCP)
# SET: 100,000 ops/sec
# Memory overhead per key type:
# String (10 byte key + 10 byte value): ~64 bytes
# Hash with 5 fields: ~200 bytes
# List with 10 items: ~200 bytes
# Set with 10 members: ~250 bytes
# Real-world WordPress benchmark:
# Before Redis: 250 requests/sec (2 concurrent, 1200ms avg)
# After Redis object cache: 1,800 requests/sec (50 concurrent, 280ms avg)
# With full-page cache: 8,500 requests/sec (100 concurrent, 12ms avg)
FAQ
1. Do I need Redis if I already have Nginx FastCGI cache?
They serve different purposes:
- Nginx FastCGI cache caches complete HTML pages for anonymous visitors. Requests that bypass the cache (logged-in users, cart pages) still hit PHP and MySQL at full speed.
- Redis object cache speeds up PHP-MySQL interaction for ALL requests, including bypassed and dynamic pages.
For maximum performance, use both: FastCGI cache for anonymous traffic + Redis for everything else.
2. Should I use TCP (127.0.0.1) or Unix socket for local Redis connections?
Unix socket is ~15-20% faster for local connections because it bypasses the TCP/IP stack. Use Unix socket when Redis and your application are on the same server.
# Redis config:
unixsocket /var/run/redis/redis-server.sock
// PHP:
$redis->connect('/var/run/redis/redis-server.sock');
3. How much RAM does Redis actually use for WordPress?
A typical WordPress site caches 5,000-50,000 objects in Redis. At ~200-500 bytes per cached object, that's 1-25 MB for the object cache. The overhead is tiny. Even 512 MB allocated to Redis will never be fully used unless you're caching enormous datasets or sessions at massive scale.
4. Is it safe to set DISABLE_WP_CRON=true when using Redis?
Yes, and you should. WordPress cron is notoriously unreliable and adds overhead to random page requests. With Redis reducing page load times, every page request becomes a potential trigger. Set DISABLE_WP_CRON=true and add a real system cron:
# crontab -e:
*/5 * * * * wget -q -O /dev/null <https://yourdomain.com/wp-cron.php?doing_wp_cron>
5. Can Redis run out of memory and crash my entire server?
Not if maxmemory is set correctly in redis.conf. Without maxmemory, Redis will use all available RAM and the OOM killer may start terminating processes. Always set maxmemory to a value that leaves adequate room for your OS, web server, PHP, and MySQL.
6. How do I clear all Redis cache without restarting?
# Clear a specific database (e.g. database 0 = WordPress cache)
redis-cli -a "yourpassword" SELECT 0
redis-cli -a "yourpassword" FLUSHDB # Clears current database only
# Clear all databases (if FLUSHALL wasn't disabled)
redis-cli -a "yourpassword" FLUSHALL
# Via WP-CLI:
wp --path=/var/www/yoursite.com/public_html redis flush
# Note: If you renamed FLUSHDB in redis.conf, you can't use the original command
# Use the renamed version instead
7. Should I use Redis as both object cache AND session storage?
Yes — use different databases:
- Database 0: WordPress object cache
- Database 1: PHP sessions
- Database 2: Laravel cache
- Database 3: Application-specific data
This keeps data organized and lets you flush caches independently without affecting sessions.
8. What's the difference between Redis 6.x and 7.x?
| Feature | Redis 6.x | Redis 7.x |
|---|---|---|
| ACL improvements | Basic | Multi-ACL files |
| Functions | No | Yes (Lua functions API) |
| Sharded Pub/Sub | No | Yes |
| Key expiry notifications | Standard | Improved |
| Memory efficiency | Good | Better |
| Recommendation | Still supported | Preferred for new setups |
Use Redis 7.x for new installations. There's no reason to use 6.x unless your OS repository doesn't provide 7.x.
Conclusion
Redis transforms what's possible on a budget VPS. Without Redis, a 2GB server running WordPress might struggle with 100 concurrent visitors. With Redis properly configured — object cache, session storage, and combined with Nginx FastCGI full-page cache — that same server can handle 1,000-10,000 concurrent visitors depending on traffic patterns.
The complete Redis setup we've covered:
- Installation — Official Ubuntu repository or latest from Redis repository
- Security — Strong password, localhost binding, dangerous command disabling, THP disabled
- Memory configuration —
maxmemoryset correctly,allkeys-lrueviction for pure cache - Persistence — Disabled for pure cache, RDB for sessions, AOF for critical data
- WordPress integration — Redis Object Cache plugin with proper
wp-config.phpsettings - PHP integration —
pconnect()with Unix socket, cache-aside pattern, rate limiting, distributed locks - Laravel integration — Multiple Redis databases for cache/session/queue
- Monitoring —
INFOcommands, hit rate calculation, slow log, automated health checks
After implementing Redis, measure your improvement with ab or wrk load testing and compare against your baseline. The results — typically 5-10× more requests per second and 60-90% lower TTFB — justify the 30 minutes of setup time many times over.
Call to Action
Have you run into a Redis configuration challenge not covered here? What's your hit rate after implementing Redis on your VPS? Share your setup in the comments — the specific combination of server specs, Redis config, and the resulting performance numbers helps everyone calibrate their own setup.
Related tutorials:
- 📌 How to Reduce Server Load and Improve Website Performance — the complete performance guide this tutorial is part of
- 📌 Nginx FastCGI Cache Setup: Full-Page Caching for WordPress — combine with Redis for maximum performance
- 📌 MySQL Performance Tuning for VPS — the other half of the backend optimization equation
Additional SEO Data
Primary Keyword: install Redis cache Linux VPS, configure Redis cache Ubuntu, Redis cache WordPress VPS
LSI Keywords: Redis object cache WordPress, PHP Redis extension install, Redis maxmemory configuration, Redis eviction policy, Redis password security, Redis Unix socket, Redis persistent connection
Semantic Keywords: in-memory cache server, key-value store Linux, Redis vs Memcached, Redis data structures, Redis replication setup, Redis Sentinel failover, Redis cluster configuration
Long Tail Keywords: how to install Redis on Ubuntu 22.04 VPS, configure Redis object cache WordPress, Redis maxmemory-policy allkeys-lru, PHP connect Redis Unix socket, WordPress Redis object cache not working
Question Keywords: How do I install Redis on Ubuntu? Is Redis safe for production? How much memory does Redis use for WordPress? What is the best Redis eviction policy? How do I check Redis cache hit rate?
Entity Keywords: Redis, Memcached, PHP-Redis extension, phpredis, Predis, WP Redis Object Cache plugin, Redis Sentinel, Redis Cluster, Ubuntu 22.04, CentOS, AlmaLinux, WP-CLI, Netdata, Laravel, WordPress
Meta Title: How to Install and Configure Redis Cache on Linux VPS: Complete Tutorial (2026)
Meta Description: Step-by-step guide to install Redis on Ubuntu/CentOS VPS, secure it properly, configure memory management and eviction policies, integrate with WordPress and PHP, and monitor performance.
Slug: how-to-install-configure-redis-cache-linux-vps
Blogger Tags: Redis, Caching, Ubuntu, Linux, VPS, WordPress, PHP, Laravel, Performance, Server Administration
Category: VPS Management, Server Administration, Web Performance, WordPress Optimization
Alt Image: Redis cache architecture diagram showing PHP application connecting to Redis in-memory store, bypassing MySQL database
Caption Image: Redis sits between your PHP application and MySQL database, serving most requests from RAM instead of disk
{
"@context": "<https://schema.org>",
"@type": "Article",
"headline": "How to Install and Configure Redis Cache on Linux VPS: Complete Tutorial",
"description": "Step-by-step guide to install Redis on Ubuntu/CentOS VPS, secure it properly, configure memory and eviction policies, integrate with WordPress, PHP, and Laravel, and monitor performance.",
"author": {
"@type": "Person",
"name": "CloudAdminHub"
},
"publisher": {
"@type": "Organization",
"name": "CloudAdminHub"
},
"datePublished": "2026-06-15",
"dateModified": "2026-06-15",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "<https://cloudadminhub.blogspot.com/2026/06/how-to-install-configure-redis-cache-linux-vps.html>"
}
}
{
"@context": "<https://schema.org>",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "How much RAM does Redis use for a WordPress site?",
"acceptedAnswer": {
"@type": "Answer",
"text": "A typical WordPress site caches 5,000 to 50,000 objects in Redis. At approximately 200-500 bytes per cached object, that's 1-25 MB for the object cache. Even allocating 512 MB to Redis, a normal WordPress site will only use 10-50 MB. The overhead is minimal compared to the performance gains."
}
},
{
"@type": "Question",
"name": "What is the best Redis eviction policy for WordPress?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Use allkeys-lru for WordPress object cache. This evicts the least recently used keys when Redis reaches its memory limit, which is ideal for a cache where all data is replaceable. If you're storing both cache data and permanent data (like job queues) in the same Redis instance, use volatile-lru instead, and set TTL only on cacheable keys."
}
},
{
"@type": "Question",
"name": "Is Redis safe to use on a public VPS?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes, if configured correctly. The critical security requirements are: (1) bind Redis to 127.0.0.1 only — never 0.0.0.0, (2) set a strong requirepass password, (3) disable dangerous commands like FLUSHALL and KEYS, (4) run Redis as the redis user, not root, and (5) verify with netstat or ss that port 6379 is not accessible from the internet."
}
}
]
}
OG Title: How to Install and Configure Redis Cache on Linux VPS (2026 Complete Guide)
OG Description: Complete Redis installation and configuration tutorial — security hardening, memory management, WordPress integration, PHP/Laravel usage, monitoring, and troubleshooting for Linux VPS.
twitter:card = summary_large_image
twitter:title = Install & Configure Redis Cache on Linux VPS — Complete Tutorial
twitter:description = From zero to production Redis: installation, security, maxmemory config, WordPress integration, PHP caching patterns, and monitoring on Ubuntu/CentOS VPS.
Recommended External Links:

Belum ada Komentar untuk "How to Install and Configure Redis Cache on Linux VPS: Complete Tutorial"
Posting Komentar