Redis Configuration Guide

Complete guide for Redis setup on Heatwave servers (production and staging).

Overview

Hybrid Architecture (Production):

  • Local Redis (127.0.0.1): Cache databases (1, 4, 5) - Geocoder, Rails cache, API cache
  • Remote Redis (Vultr): Shared databases (0, 2, 3) - Sessions, Action Cable, Sidekiq

Single Server (Staging):

  • Local Redis (127.0.0.1): All databases (0-5) - Everything runs locally
  • Unix socket enabled for better performance
  • Persistence enabled - Sessions/Sidekiq need to survive restarts

Database Assignments

Database Service Production Location Staging Location
0 Sessions Remote (Vultr) Local (127.0.0.1)
1 Geocoder cache Local (127.0.0.1) Local (127.0.0.1)
2 Action Cable Remote (Vultr) Local (127.0.0.1)
3 Sidekiq Remote (Vultr) Local (127.0.0.1)
4 Rails cache Local (127.0.0.1) Local (127.0.0.1)
5 API cache Local (127.0.0.1) Local (127.0.0.1)

Why Hybrid in Production:

  • Cache databases (1, 4, 5) are deterministic - no sharing needed, faster locally
  • Shared databases (0, 2, 3) must be shared across multiple app servers

Production Setup (App Servers)

Step 1: Install Redis

sudo apt-get update
sudo apt-get install -y redis-server
redis-server --version

Step 2: Configure Redis for Cache-Only

Edit /etc/redis/redis.conf:

# Network - localhost only
bind 127.0.0.1 ::1
protected-mode yes

# Memory management
maxmemory 1gb  # Adjust based on available RAM (see sizing guide below)
maxmemory-policy allkeys-lru

# Disable persistence (cache is ephemeral)
save ""
appendonly no

# Performance
tcp-keepalive 300
timeout 300
maxclients 10000

# Logging
loglevel notice
logfile /var/log/redis/redis-server.log

Memory Sizing Guide:

  • Check available RAM: free -h
  • Use 10-20% of available RAM, max 2GB for cache-only
  • Example: 7GB available → use 1GB for Redis

Step 3: Create Log Directory

sudo mkdir -p /var/log/redis
sudo chown redis:redis /var/log/redis
sudo chmod 755 /var/log/redis

Step 4: Start Redis

sudo systemctl enable redis-server
sudo systemctl start redis-server
sudo systemctl status redis-server

# Test connection
redis-cli ping
# Should return: PONG

Step 5: Verify Cache Databases

redis-cli -n 1 ping  # Geocoder cache
redis-cli -n 4 ping  # Rails cache
redis-cli -n 5 ping  # API cache

All should return PONG.


Staging Setup (Single Server)

Step 1: Install Redis

sudo apt-get update
sudo apt-get install -y redis-server

Step 2: Configure Redis

Edit /etc/redis/redis.conf:

# Network - localhost only
bind 127.0.0.1 ::1
protected-mode yes

# Unix socket (better performance for local connections)
unixsocket /var/run/redis/redis-server.sock
unixsocketperm 770

# Memory management
maxmemory 512mb
maxmemory-policy allkeys-lru

# Persistence enabled (sessions/Sidekiq need it)
# Keep default save points or explicitly set:
save 3600 1 300 100 60 10000
appendonly no  # RDB persistence is sufficient

# Password authentication (OPTIONAL for localhost-only)
# Since Redis is bound to 127.0.0.1 only, password is optional
# Only add if you want defense-in-depth or consistency with production patterns
# requirepass entreat8GRANDAM-ply

# Logging
loglevel notice
logfile /var/log/redis/redis-server.log

Step 3: Create Socket Directory

sudo mkdir -p /var/run/redis
sudo chown redis:redis /var/run/redis
sudo chmod 755 /var/run/redis

Step 3.5: Add Deploy User to Redis Group (REQUIRED)

Critical: The deploy user must be in the redis group to access the Unix socket during Capistrano deployments.

# Add deploy user to redis group
sudo usermod -a -G redis deploy

# Verify the user is in the group
groups deploy
# Should show: deploy redis ... (redis should be listed)

# Note: User must log out and back in (or restart session) for group changes to take effect
# For systemd services, restart the service after adding the user to the group

Why this is needed:

  • Unix socket permissions are 770 (owner and group can read/write)
  • Socket is owned by redis:redis
  • deploy user needs group membership to connect during rake db:migrate and other deployment tasks

Step 4: Start Redis

sudo systemctl enable redis-server
sudo systemctl restart redis-server

# Test Unix socket (with password if configured)
redis-cli -s /var/run/redis/redis-server.sock -a entreat8GRANDAM-ply ping
# Or without password: redis-cli -s /var/run/redis/redis-server.sock ping

# Test TCP (with password if configured)
redis-cli -a entreat8GRANDAM-ply ping
# Or without password: redis-cli ping

Both should return PONG.


Configuration Files

config/redis_consolidated.yml

This file defines Redis connection settings per environment:

default: &default
  host: 127.0.0.1
  port: 6379
  ssl: false

staging:
  <<: *default
  username: entreat8GRANDAM-ply
  password:  # Password from redis.conf requirepass

production:
  host: vultr-prod-17a82860-f346-40c1-967a-e683814f57ea-vultr-prod-a2b0.vultrdb.com
  port: 16752
  username: default
  password: AVNS_SpT2e_ummAiKRKiPjuT@vultr-prod-17a82860-f346-40c1-967a-e683814f57ea-vultr-prod-a2b0
  ssl: true

Code Configuration

The RedisConfig class (config/initializers/100_redis_config.rb) automatically:

  • Routes cache databases (1, 4, 5) to local Redis in production
  • Routes shared databases (0, 2, 3) to remote Redis in production
  • Detects and uses Unix socket if available (staging)
  • Falls back to TCP if socket not found
  • Handles password authentication for both TCP and Unix socket

No manual configuration needed - the code handles routing automatically.


Verification

Production (App Servers)

# Check local Redis status
sudo systemctl status redis-server
redis-cli info memory

# Test cache databases
redis-cli -n 1 dbsize  # Geocoder cache
redis-cli -n 4 dbsize  # Rails cache
redis-cli -n 5 dbsize  # API cache

# In Rails console
rails console
Rails.cache.write('test', 'value', expires_in: 1.minute)
Rails.cache.read('test')  # Should return "value"

Staging

# Check Redis status
sudo systemctl status redis-server

# Test Unix socket (with password if configured)
redis-cli -s /var/run/redis/redis-server.sock -a entreat8GRANDAM-ply ping
# Or without password: redis-cli -s /var/run/redis/redis-server.sock ping

# Verify socket exists
ls -la /var/run/redis/redis-server.sock
# Should show: srwxrwx--- redis redis

# Check persistence (with password if configured)
redis-cli -a entreat8GRANDAM-ply CONFIG GET save
# Or without password: redis-cli CONFIG GET save

Testing Unix Socket Access (Deploy User)

After adding deploy user to redis group, test access:

# Switch to deploy user
sudo su - deploy

# Quick test: Direct socket access
redis-cli -s /var/run/redis/redis-server.sock ping
# Should return: PONG

# Comprehensive test: Run the test script
cd /var/www/heatwave/current
RAILS_ENV=staging bundle exec rails runner script/test_redis_socket_access.rb

# This will test:
# - User group membership
# - Socket file permissions
# - Direct Unix socket connection
# - Sidekiq connection (critical for deployments)
# - Rails cache connection
# - Sessions connection
# - Action Cable connection
# - Geocoder connection

Expected output:

  • ✅ All connections should succeed
  • ✅ User should be in redis group
  • ✅ Socket should be readable/writable
  • ✅ Sidekiq job enqueue should work (this is what failed during migration)

Troubleshooting

Redis Won't Start

# Check logs
sudo journalctl -u redis-server -n 50

# Check if port is in use
sudo lsof -i :6379

# Test config syntax
redis-server /etc/redis/redis.conf --test-memory 1

Connection Refused

# Verify Redis is running
sudo systemctl status redis-server

# Test connection
redis-cli ping

# Check Rails logs for connection errors
tail -f log/production.log | grep -i redis

Memory Issues

# Check current usage
redis-cli info memory

# If hitting maxmemory, increase in redis.conf:
# maxmemory 1gb
# Then restart: sudo systemctl restart redis-server

Unix Socket Permission Denied (Staging)

This error occurs when the deploy user cannot access the Unix socket during deployments.

# Check socket permissions
ls -la /var/run/redis/redis-server.sock
# Should show: srwxrwx--- redis redis

# Check if deploy user is in redis group
groups deploy
# Should include 'redis' in the output

# Fix: Add deploy user to redis group (REQUIRED)
sudo usermod -a -G redis deploy

# Verify group membership
groups deploy
# Should show: deploy : deploy sudo redis docker

# After adding to group:
# - For interactive testing: Use `newgrp redis` to activate the group in current session
# - For Capistrano deployments: Group membership is automatically active (each command runs in new shell)
# - For systemd services: Restart the service after adding user to group

# Quick test (after adding to group):
newgrp redis
redis-cli -s /var/run/redis/redis-server.sock ping
# Should return: PONG

# If socket permissions are wrong, fix them:
sudo chown redis:redis /var/run/redis/redis-server.sock
sudo chmod 770 /var/run/redis/redis-server.sock
sudo systemctl restart redis-server

Password Authentication Issues

Note: Password is optional for localhost-only Redis. If you don't use a password, remove requirepass from redis.conf and remove password from config/redis_consolidated.yml.

If you do use a password:

  • Password works the same for TCP and Unix socket - the Redis client handles AUTH automatically
  • If you see authentication errors:
    1. Verify password in /etc/redis/redis.conf matches config/redis_consolidated.yml
    2. Check Rails logs for connection URL (should include password if configured)
    3. Test manually: redis-cli -a <password> ping

Security Checklist

Production (Cache-Only)

  • ✅ Redis bound to 127.0.0.1 only (not accessible externally)
  • ✅ No password needed (localhost-only)
  • ✅ Protected mode enabled
  • ✅ Firewall doesn't expose port 6379
  • ✅ Runs as redis user (not root)
  • ✅ Persistence disabled (cache is ephemeral)

Staging (Full Redis)

  • ✅ Redis bound to 127.0.0.1 only
  • ⚠️ Password authentication (optional - not needed for localhost-only, adds unnecessary complexity)
  • ✅ Protected mode enabled
  • ✅ Unix socket with proper permissions (770)
  • ✅ Persistence enabled (sessions/Sidekiq need it)

Monitoring

Check Redis Status

# Service status
sudo systemctl status redis-server

# Memory usage
redis-cli info memory

# Database sizes
redis-cli -n 0 dbsize  # Sessions
redis-cli -n 1 dbsize  # Geocoder cache
redis-cli -n 4 dbsize  # Rails cache
redis-cli -n 5 dbsize  # API cache

# View logs
sudo tail -f /var/log/redis/redis-server.log

Rails Logs

Look for these messages on startup:

Redis cache store initialized successfully: redis://127.0.0.1:6379/4
Redis API cache store initialized successfully: redis://127.0.0.1:6379/5

For staging with Unix socket:

Redis cache store initialized successfully: unix:///var/run/redis/redis-server.sock?db=4

Benefits

Production Hybrid Setup

  1. Cache databases (1, 4, 5) local:

    • ✅ No network dependency = no connection failures
    • ✅ Sub-millisecond latency
    • ✅ Eliminates cache-related incidents
  2. Shared databases (0, 2, 3) remote:

    • ✅ Sessions work across all app servers
    • ✅ Sidekiq jobs can be processed by any worker
    • ✅ Action Cable pub/sub works across servers

Staging Single Server

  1. All databases local:
    • ✅ Simple setup (one server handles everything)
    • ✅ Unix socket for optimal performance
    • ✅ Persistence for sessions/Sidekiq

Rollback Plan

If issues arise:

  1. Revert config/initializers/100_redis_config.rb to previous version
  2. Restart Rails application
  3. All databases will use remote Redis again (production) or continue using local (staging)

No need to uninstall local Redis - it just won't be used.


Quick Reference

Production App Server Commands

# Install
sudo apt-get install -y redis-server

# Configure (edit /etc/redis/redis.conf)
# - Set maxmemory 1gb
# - Set maxmemory-policy allkeys-lru
# - Disable persistence (save "", appendonly no)

# Start
sudo systemctl enable redis-server
sudo systemctl start redis-server

# Test
redis-cli ping

Staging Server Commands

# Install
sudo apt-get install -y redis-server

# Configure (edit /etc/redis/redis.conf)
# - Enable Unix socket (unixsocket /var/run/redis/redis-server.sock)
# - Set unixsocketperm 770
# - Set maxmemory 512mb
# - Keep persistence enabled
# - Password is optional (not needed for localhost-only)

# Create socket directory
sudo mkdir -p /var/run/redis
sudo chown redis:redis /var/run/redis

# Start
sudo systemctl enable redis-server
sudo systemctl restart redis-server

# Test (with password if configured)
redis-cli -s /var/run/redis/redis-server.sock -a <password> ping
# Or without password: redis-cli -s /var/run/redis/redis-server.sock ping