Set up Nginx with Redis cluster caching to achieve high availability and optimized performance. This advanced configuration includes cluster setup, failover testing, and performance tuning for production environments.
Prerequisites
- Multiple servers (minimum 6 for Redis cluster)
- Root or sudo access
- Basic understanding of Redis and Nginx
- Network connectivity between cluster nodes
What this solves
Nginx Redis cluster caching provides high availability caching with automatic failover, eliminating single points of failure while delivering exceptional performance. This setup scales horizontally across multiple Redis nodes and maintains cache consistency during node failures, making it ideal for high-traffic applications requiring zero-downtime caching.
Step-by-step configuration
Install Redis cluster dependencies
Install Redis server and cluster tools on all designated Redis nodes. We'll create a 6-node cluster with 3 masters and 3 replicas for optimal redundancy.
sudo apt update
sudo apt install -y redis-server redis-tools build-essential
Configure Redis cluster nodes
Configure each Redis instance for cluster mode. Repeat this configuration on all 6 nodes, changing the port for each instance.
port 7001
cluster-enabled yes
cluster-config-file nodes-7001.conf
cluster-node-timeout 15000
appendonly yes
bind 0.0.0.0
protected-mode no
dir /var/lib/redis/7001
logfile /var/log/redis/redis-7001.log
pidfile /var/run/redis/redis-7001.pid
daemonize yes
maxmemory 2gb
maxmemory-policy allkeys-lru
tcp-keepalive 60
save 900 1
save 300 10
save 60 10000
Create Redis data directories
Create the required directories for each Redis instance and set proper ownership.
sudo mkdir -p /var/lib/redis/7001 /var/lib/redis/7002 /var/lib/redis/7003
sudo mkdir -p /var/log/redis /var/run/redis
sudo chown -R redis:redis /var/lib/redis /var/log/redis /var/run/redis
sudo chmod 755 /var/lib/redis/700{1,2,3}
Start Redis cluster instances
Start all Redis instances across your cluster nodes. Run these commands on each server with the appropriate configuration files.
sudo redis-server /etc/redis/redis-7001.conf
sudo redis-server /etc/redis/redis-7002.conf
sudo redis-server /etc/redis/redis-7003.conf
Verify instances are running
sudo netstat -tulpn | grep redis
Initialize Redis cluster
Create the Redis cluster with 3 master nodes and 3 replica nodes. Replace the IP addresses with your actual server IPs.
redis-cli --cluster create \
203.0.113.10:7001 203.0.113.11:7001 203.0.113.12:7001 \
203.0.113.10:7002 203.0.113.11:7002 203.0.113.12:7002 \
--cluster-replicas 1
Type 'yes' when prompted to accept the cluster configuration
Install Nginx Redis module
Install Nginx with the Redis module for caching capabilities. The redis2-nginx-module provides Redis cluster support.
sudo apt install -y nginx-module-redis2 nginx-module-set-misc nginx-module-echo
sudo nginx -t
Configure Nginx Redis caching
Configure Nginx to use Redis cluster for caching with automatic failover and load balancing across cluster nodes.
load_module modules/ngx_http_redis2_module.so;
load_module modules/ngx_http_set_misc_module.so;
load_module modules/ngx_http_echo_module.so;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Redis cluster upstream
upstream redis_cluster {
server 203.0.113.10:7001 max_fails=3 fail_timeout=30s;
server 203.0.113.11:7001 max_fails=3 fail_timeout=30s;
server 203.0.113.12:7001 max_fails=3 fail_timeout=30s;
keepalive 32;
}
# Backend application servers
upstream app_backend {
server 203.0.113.20:8080;
server 203.0.113.21:8080;
keepalive 16;
}
# Cache configuration
redis2_query_timeout 5s;
redis2_connect_timeout 3s;
redis2_read_timeout 5s;
redis2_send_timeout 3s;
# Logging
log_format cache_log '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'cache_status=$upstream_cache_status '
'response_time=$request_time';
server {
listen 80;
server_name example.com;
access_log /var/log/nginx/cache.log cache_log;
# Cache key generation
set $cache_key "$scheme$request_method$host$request_uri";
location / {
# Try cache first
access_by_lua_block {
local cache_key = ngx.var.cache_key
local res = ngx.location.capture("/redis_get", {
args = { key = cache_key }
})
if res.status == 200 and res.body ~= "" and res.body ~= "$"-1" then
ngx.header.content_type = "application/json"
ngx.header["X-Cache-Status"] = "HIT"
ngx.say(res.body)
ngx.exit(200)
end
}
# Cache miss - proxy to backend
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Cache the response
access_by_lua_block {
local cache_key = ngx.var.cache_key
local cache_ttl = 300 -- 5 minutes
-- Store in Redis cluster
ngx.location.capture("/redis_set", {
args = {
key = cache_key,
value = ngx.var.response_body,
expire = cache_ttl
}
})
}
add_header X-Cache-Status "MISS";
}
# Redis GET operation
location /redis_get {
internal;
redis2_query get $arg_key;
redis2_pass redis_cluster;
}
# Redis SET operation with expiration
location /redis_set {
internal;
redis2_query setex $arg_key $arg_expire $arg_value;
redis2_pass redis_cluster;
}
# Cache invalidation endpoint
location /cache/invalidate {
allow 203.0.113.0/24;
deny all;
redis2_query del $arg_key;
redis2_pass redis_cluster;
add_header Content-Type text/plain;
return 200 "Cache invalidated";
}
# Health check endpoint
location /health {
access_log off;
return 200 "OK";
add_header Content-Type text/plain;
}
}
}
Configure cache optimization settings
Create an optimized caching configuration with intelligent TTL management and cache warming strategies.
# Cache zones for different content types
proxy_cache_path /var/cache/nginx/api levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m use_temp_path=off;
proxy_cache_path /var/cache/nginx/static levels=1:2 keys_zone=static_cache:10m max_size=2g inactive=24h use_temp_path=off;
Cache key optimization
map $request_uri $cache_ttl {
~*/api/users 300; # 5 minutes for user data
~*/api/products 1800; # 30 minutes for product data
~*/api/config 3600; # 1 hour for configuration
default 600; # 10 minutes default
}
map $http_accept $content_type_suffix {
~*json ".json";
~*xml ".xml";
default "";
}
Intelligent cache bypass
map $http_cache_control $bypass_cache {
~*no-cache 1;
~*no-store 1;
~*private 1;
default 0;
}
Cache warming for popular endpoints
map $request_uri $should_warm {
~/api/popular/. 1;
~/api/trending/. 1;
default 0;
}
Set up cache monitoring and metrics
Configure monitoring to track cache performance, hit rates, and Redis cluster health.
# Metrics collection for cache performance
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 203.0.113.0/24;
deny all;
}
Redis cluster health check
location /redis_health {
access_log off;
allow 127.0.0.1;
allow 203.0.113.0/24;
deny all;
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:set_timeouts(1000, 1000, 1000)
local nodes = {
{"203.0.113.10", 7001},
{"203.0.113.11", 7001},
{"203.0.113.12", 7001}
}
local healthy_nodes = 0
for _, node in ipairs(nodes) do
local ok, err = red:connect(node[1], node[2])
if ok then
local res, err = red:ping()
if res == "PONG" then
healthy_nodes = healthy_nodes + 1
end
red:close()
end
end
ngx.header.content_type = "application/json"
ngx.say('{"healthy_nodes":' .. healthy_nodes .. ',"total_nodes":' .. #nodes .. '}')
}
}
Create cache directories and set permissions
Create the necessary cache directories with proper permissions for Nginx worker processes.
sudo mkdir -p /var/cache/nginx/{api,static}
sudo chown -R nginx:nginx /var/cache/nginx
sudo chmod 755 /var/cache/nginx /var/cache/nginx/{api,static}
Configure systemd services
Set up systemd services for Redis cluster instances to ensure automatic startup and proper management.
[Unit]
Description=Redis Cluster Instance %i
After=network.target
[Service]
Type=forking
User=redis
Group=redis
ExecStart=/usr/bin/redis-server /etc/redis/redis-%i.conf
ExecStop=/bin/kill -s QUIT $MAINPID
TimeoutStopSec=0
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Enable and start all services
Enable the Redis cluster instances and Nginx to start automatically on boot.
sudo systemctl daemon-reload
sudo systemctl enable redis-cluster@7001 redis-cluster@7002 redis-cluster@7003
sudo systemctl start redis-cluster@7001 redis-cluster@7002 redis-cluster@7003
sudo systemctl enable nginx
sudo systemctl restart nginx
Performance tuning and optimization
Configure Redis memory optimization
Optimize Redis memory settings for high-performance caching with appropriate eviction policies.
# Add to each Redis configuration file
echo "tcp-backlog 511" | sudo tee -a /etc/redis/redis-7001.conf
echo "timeout 300" | sudo tee -a /etc/redis/redis-7001.conf
echo "tcp-keepalive 60" | sudo tee -a /etc/redis/redis-7001.conf
echo "maxclients 10000" | sudo tee -a /etc/redis/redis-7001.conf
Restart Redis instances
sudo systemctl restart redis-cluster@7001
Optimize Nginx worker configuration
Configure Nginx workers and connections for optimal performance with Redis cluster caching.
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 1000;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# Connection pooling to Redis
upstream_keepalive_requests 1000;
upstream_keepalive_timeout 60s;
}
High availability and failover testing
Test automatic failover
Simulate node failures to verify automatic failover functionality in your Redis cluster.
# Check cluster status before testing
redis-cli -c -h 203.0.113.10 -p 7001 cluster nodes
Stop one master node to test failover
sudo systemctl stop redis-cluster@7001
Verify cluster rebalancing
redis-cli -c -h 203.0.113.11 -p 7001 cluster nodes
Test cache operations still work
curl -H "Cache-Control: no-cache" http://example.com/api/test
Restart the stopped node
sudo systemctl start redis-cluster@7001
Monitor cluster health
Set up continuous monitoring to track cluster health and cache performance metrics.
# Create monitoring script
sudo tee /usr/local/bin/monitor-redis-cluster.sh << 'EOF'
#!/bin/bash
LOG_FILE="/var/log/redis/cluster-health.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
for port in 7001 7002 7003; do
for host in 203.0.113.10 203.0.113.11 203.0.113.12; do
if redis-cli -h $host -p $port ping > /dev/null 2>&1; then
echo "$DATE - $host:$port OK" >> $LOG_FILE
else
echo "$DATE - $host:$port FAILED" >> $LOG_FILE
fi
done
done
Check cluster state
redis-cli -c -h 203.0.113.10 -p 7001 cluster info >> $LOG_FILE
EOF
sudo chmod +x /usr/local/bin/monitor-redis-cluster.sh
Add to crontab for every 2 minutes
echo "/2 * /usr/local/bin/monitor-redis-cluster.sh" | sudo crontab -
Verify your setup
# Check Redis cluster status
redis-cli -c -h 203.0.113.10 -p 7001 cluster info
redis-cli -c -h 203.0.113.10 -p 7001 cluster nodes
Test cache functionality
curl -v http://example.com/health
curl -v http://example.com/redis_health
Check Nginx status and cache statistics
curl http://127.0.0.1/nginx_status
Verify cache hit/miss in logs
sudo tail -f /var/log/nginx/cache.log
Test cache invalidation
curl "http://example.com/cache/invalidate?key=test_key"
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Cluster formation fails | Network connectivity or firewall | Check ports 7001-7003 and 17001-17003 are open |
| Cache misses too frequent | TTL too low or memory pressure | Increase maxmemory and adjust TTL values |
| Redis connection timeouts | High latency or connection limits | Increase redis2_*_timeout values and maxclients |
| Nginx fails to start | Missing Redis modules | Install nginx-module-redis2 and nginx-module-set-misc |
| Permission denied errors | Incorrect cache directory ownership | Run chown -R nginx:nginx /var/cache/nginx |
| Failover not working | Insufficient replica nodes | Ensure cluster-replicas is set to 1 minimum |
Next steps
- Monitor Nginx performance with Prometheus and Grafana for comprehensive performance tracking
- Configure Redis cluster SSL authentication for enhanced security
- Setup Prometheus Blackbox Exporter for endpoint monitoring to monitor cache endpoints
- Optimize Nginx performance for high-traffic websites for advanced performance tuning
- Configure Nginx rate limiting and DDoS protection to secure your cached endpoints
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Cleanup function
cleanup() {
echo -e "${RED}[ERROR] Installation failed. Cleaning up...${NC}"
systemctl stop nginx 2>/dev/null || true
systemctl stop redis-server 2>/dev/null || true
pkill -f redis-server 2>/dev/null || true
}
trap cleanup ERR
# Usage
usage() {
echo "Usage: $0 [redis_nodes] [backend_servers]"
echo " redis_nodes: comma-separated list of Redis node IPs (default: 127.0.0.1,127.0.0.2,127.0.0.3)"
echo " backend_servers: comma-separated list of backend server IPs (default: 127.0.0.1:8080)"
echo "Example: $0 '10.0.1.1,10.0.1.2,10.0.1.3' '10.0.2.1:8080,10.0.2.2:8080'"
exit 1
}
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root${NC}"
exit 1
fi
# Parse arguments
REDIS_NODES="${1:-127.0.0.1,127.0.0.2,127.0.0.3}"
BACKEND_SERVERS="${2:-127.0.0.1:8080}"
# Detect distribution
echo -e "${YELLOW}[1/10] Detecting distribution...${NC}"
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update"
NGINX_USER="www-data"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
NGINX_USER="nginx"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
NGINX_USER="nginx"
;;
*)
echo -e "${RED}Unsupported distro: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}Cannot detect distribution${NC}"
exit 1
fi
echo -e "${GREEN}Detected: $PRETTY_NAME${NC}"
# Update package manager
echo -e "${YELLOW}[2/10] Updating package manager...${NC}"
$PKG_UPDATE
# Install Redis and dependencies
echo -e "${YELLOW}[3/10] Installing Redis and build tools...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL redis-server redis-tools build-essential
else
$PKG_INSTALL redis redis-tools gcc make
systemctl enable redis
fi
# Install Nginx
echo -e "${YELLOW}[4/10] Installing Nginx...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL nginx nginx-module-redis2 nginx-module-set-misc nginx-module-echo 2>/dev/null || \
$PKG_INSTALL nginx libnginx-mod-http-redis2 libnginx-mod-http-set-misc libnginx-mod-http-echo 2>/dev/null || \
$PKG_INSTALL nginx
else
$PKG_INSTALL nginx nginx-mod-stream 2>/dev/null || $PKG_INSTALL nginx
systemctl enable nginx
fi
# Create Redis directories
echo -e "${YELLOW}[5/10] Creating Redis directories...${NC}"
mkdir -p /var/lib/redis/{7001,7002,7003}
mkdir -p /var/log/redis /var/run/redis /etc/redis
chown -R redis:redis /var/lib/redis /var/log/redis /var/run/redis
chmod 755 /var/lib/redis/700{1,2,3}
# Configure Redis cluster nodes
echo -e "${YELLOW}[6/10] Configuring Redis cluster...${NC}"
for port in 7001 7002 7003; do
cat > /etc/redis/redis-${port}.conf << EOF
port $port
cluster-enabled yes
cluster-config-file nodes-${port}.conf
cluster-node-timeout 15000
appendonly yes
bind 0.0.0.0
protected-mode no
dir /var/lib/redis/$port
logfile /var/log/redis/redis-${port}.log
pidfile /var/run/redis/redis-${port}.pid
daemonize yes
maxmemory 2gb
maxmemory-policy allkeys-lru
tcp-keepalive 60
save 900 1
save 300 10
save 60 10000
EOF
chmod 644 /etc/redis/redis-${port}.conf
chown redis:redis /etc/redis/redis-${port}.conf
done
# Start Redis instances
echo -e "${YELLOW}[7/10] Starting Redis instances...${NC}"
systemctl stop redis-server 2>/dev/null || systemctl stop redis 2>/dev/null || true
for port in 7001 7002 7003; do
sudo -u redis redis-server /etc/redis/redis-${port}.conf
sleep 2
done
# Verify Redis instances
echo -e "${YELLOW}[8/10] Verifying Redis instances...${NC}"
sleep 5
for port in 7001 7002 7003; do
if ! netstat -tulpn | grep -q ":$port"; then
echo -e "${RED}Redis instance on port $port failed to start${NC}"
exit 1
fi
done
# Configure Nginx
echo -e "${YELLOW}[9/10] Configuring Nginx...${NC}"
# Build upstream configuration
REDIS_UPSTREAM=""
IFS=',' read -ra NODES <<< "$REDIS_NODES"
for node in "${NODES[@]}"; do
REDIS_UPSTREAM+="\n server ${node}:7001 max_fails=3 fail_timeout=30s;"
done
BACKEND_UPSTREAM=""
IFS=',' read -ra BACKENDS <<< "$BACKEND_SERVERS"
for backend in "${BACKENDS[@]}"; do
BACKEND_UPSTREAM+="\n server ${backend};"
done
# Create Nginx configuration
cat > /etc/nginx/nginx.conf << EOF
user $NGINX_USER;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
upstream redis_cluster {$REDIS_UPSTREAM
keepalive 32;
}
upstream app_backend {$BACKEND_UPSTREAM
keepalive 16;
}
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=redis_cache:10m max_size=10g
inactive=60m use_temp_path=off;
log_format cache_log '\$remote_addr - \$remote_user [\$time_local] '
'"\$request" \$status \$body_bytes_sent '
'"\$http_referer" "\$http_user_agent" '
'cache_status=\$upstream_cache_status';
server {
listen 80 default_server;
server_name _;
access_log /var/log/nginx/access.log cache_log;
error_log /var/log/nginx/error.log;
location /api/ {
set \$redis_key "\$scheme\$request_method\$host\$request_uri";
proxy_cache redis_cache;
proxy_cache_key \$redis_key;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cache-Status \$upstream_cache_status;
proxy_pass http://app_backend;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
}
location / {
proxy_pass http://app_backend;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
}
location /nginx-status {
stub_status;
allow 127.0.0.1;
deny all;
}
}
}
EOF
# Create cache directory
mkdir -p /var/cache/nginx
chown -R $NGINX_USER:$NGINX_USER /var/cache/nginx
chmod 755 /var/cache/nginx
# Test and start Nginx
nginx -t
systemctl restart nginx
systemctl enable nginx
# Configure firewall
echo -e "${YELLOW}[10/10] Configuring firewall...${NC}"
if command -v ufw &> /dev/null; then
ufw allow 80/tcp
ufw allow 7001:7003/tcp
elif command -v firewall-cmd &> /dev/null; then
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=7001-7003/tcp
firewall-cmd --reload
fi
# Verify installation
echo -e "${GREEN}[SUCCESS] Verifying installation...${NC}"
if systemctl is-active --quiet nginx; then
echo -e "${GREEN}✓ Nginx is running${NC}"
else
echo -e "${RED}✗ Nginx is not running${NC}"
exit 1
fi
redis_running=0
for port in 7001 7002 7003; do
if redis-cli -p $port ping 2>/dev/null | grep -q PONG; then
((redis_running++))
fi
done
if [[ $redis_running -eq 3 ]]; then
echo -e "${GREEN}✓ All Redis instances are running${NC}"
else
echo -e "${YELLOW}⚠ Only $redis_running/3 Redis instances are running${NC}"
fi
echo -e "${GREEN}Installation completed successfully!${NC}"
echo -e "${YELLOW}Next steps:${NC}"
echo "1. Initialize Redis cluster with: redis-cli --cluster create [node_ips] --cluster-replicas 1"
echo "2. Test caching: curl -H 'Host: yourdomain.com' http://localhost/api/test"
echo "3. Monitor: tail -f /var/log/nginx/access.log"
Review the script before running. Execute with: bash install.sh