Setting Up Redis for Web Application Session Storage
File-based sessions don't scale horizontally — with two web servers, a user landing on the second server loses the session from the first. Redis solves this: centralized session storage accessible to all servers. Additionally, Redis is faster than file system under high load and allows setting TTL on each session.
Redis Configuration for Sessions
Sessions are data you can't lose (user logs out). Redis settings for sessions differ from cache settings:
# Persistence mandatory for sessions
appendonly yes
appendfsync everysec
# Eviction policy: don't touch TTL-less keys
# If Redis only for sessions:
maxmemory-policy volatile-lru
# If Redis shared (cache + sessions):
# Store sessions with TTL, policy volatile-lru protects from eviction
Separate Redis instance for sessions (recommended):
# /etc/redis/redis-sessions.conf
port 6380
bind 127.0.0.1
requirepass SessionsRedisPassword
maxmemory 512mb
maxmemory-policy volatile-lru
appendonly yes
appendfsync everysec
databases 1
Laravel Session Driver
config/session.php:
return [
'driver' => env('SESSION_DRIVER', 'redis'),
'lifetime' => env('SESSION_LIFETIME', 120),
'expire_on_close' => false,
'encrypt' => env('SESSION_ENCRYPT', true), // Session data encryption
'files' => storage_path('framework/sessions'),
'connection' => 'sessions', // Separate redis connection
'table' => 'sessions',
'store' => null,
'lottery' => [2, 100],
'cookie' => env('SESSION_COOKIE', 'laravel_session'),
'path' => '/',
'domain' => env('SESSION_DOMAIN'),
'secure' => env('SESSION_SECURE_COOKIE', true), // HTTPS only
'http_only' => true,
'same_site' => 'lax',
];
Add sessions connection in config/database.php:
'redis' => [
'sessions' => [
'host' => env('REDIS_SESSION_HOST', '127.0.0.1'),
'password' => env('REDIS_SESSION_PASSWORD'),
'port' => env('REDIS_SESSION_PORT', '6380'),
'database' => 0,
'read_timeout' => 60,
'persistent' => false,
],
],
.env:
SESSION_DRIVER=redis
SESSION_LIFETIME=120
SESSION_ENCRYPT=true
REDIS_SESSION_HOST=127.0.0.1
REDIS_SESSION_PASSWORD=SessionsRedisPassword
REDIS_SESSION_PORT=6380
Session Data Encryption
SESSION_ENCRYPT=true enables session content encryption via APP_KEY. Even with Redis access, session data is unreadable without the app key.
Important: APP_KEY must be unique and securely stored. Key rotation invalidates all sessions.
PHP-FPM + Redis Sessions (without Laravel)
Native PHP configuration for session storage in Redis:
# /etc/php/8.2/fpm/php.ini or conf.d/redis-sessions.ini
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6380?auth=SessionsRedisPassword&database=0&weight=1&timeout=2.5"
session.gc_maxlifetime = 7200
session.cookie_secure = 1
session.cookie_httponly = 1
session.cookie_samesite = Lax
session.use_strict_mode = 1
For Redis Cluster:
session.save_path = "tcp://redis-node1:6379?auth=password,tcp://redis-node2:6379?auth=password,tcp://redis-node3:6379?auth=password"
Managing Active Sessions
Redis lets you view and force-terminate sessions — hard to do with file sessions.
class SessionManager
{
private Redis $redis;
private string $prefix = 'laravel_session:';
public function getUserSessions(int $userId): array
{
// If saving user_id -> session_id mapping on creation
$sessionIds = $this->redis->smembers("user_sessions:{$userId}");
$sessions = [];
foreach ($sessionIds as $sessionId) {
$data = $this->redis->get($this->prefix . $sessionId);
if ($data) {
$ttl = $this->redis->ttl($this->prefix . $sessionId);
$sessions[] = [
'id' => $sessionId,
'ttl' => $ttl,
'data' => $this->decodeSession($data),
];
}
}
return $sessions;
}
public function invalidateUserSessions(int $userId): void
{
$sessionIds = $this->redis->smembers("user_sessions:{$userId}");
$pipe = $this->redis->pipeline();
foreach ($sessionIds as $sessionId) {
$pipe->del($this->prefix . $sessionId);
}
$pipe->del("user_sessions:{$userId}");
$pipe->execute();
}
}
On login handler — save user to session mapping:
// After successful login
$this->redis->sadd("user_sessions:{$user->id}", session()->getId());
$this->redis->expire("user_sessions:{$user->id}", config('session.lifetime') * 60);
Sticky Sessions vs. Centralized
Some use sticky sessions (nginx ip_hash) instead of centralized storage. Technical debt: one server dies — all sessions lost, load balancing uneven.
Redis sessions work correctly: any server serves any user.
Session Monitoring
# Active sessions count
redis-cli -p 6380 -a SessionsRedisPassword DBSIZE
# Average session TTL
redis-cli -p 6380 -a SessionsRedisPassword INFO keyspace
# Memory usage
redis-cli -p 6380 -a SessionsRedisPassword INFO memory | grep used_memory_human
# Individual session size (debug bloated sessions)
redis-cli -p 6380 -a SessionsRedisPassword DEBUG OBJECT "laravel_session:abc123"
If sessions unexpectedly large — check what's stored. Common mistake: storing large object collections instead of just IDs.
Timeline
Setting up Redis Sessions for Laravel app on one or multiple servers — 4–8 hours. Includes separate Redis instance setup, encryption config, session correctness verification post-deploy.







