Redis for Cache, Session and Queue
One Redis server can back the cache, sessions and the queue; separate them, because clearing the cache does not ask.
CACHE_STORE=redis / SESSION_DRIVER=redis / QUEUE_CONNECTION=redisRedis is a single in-memory key/value server, and three separate parts of Laravel can sit on it: the cache store, the session driver and the queue. They are configured independently, which is the part that catches people out.
Three settings, one server
Connections are named in the redis array of config/database.php. The shipped file defines default on database REDIS_DB (0) and cache on REDIS_CACHE_DB (1). CACHE_STORE=redis uses the connection named by REDIS_CACHE_CONNECTION in config/cache.php, which is cache. SESSION_DRIVER=redis uses the connection in SESSION_CONNECTION. QUEUE_CONNECTION=redis uses REDIS_QUEUE_CONNECTION, which defaults to default. Give each job its own database number, or at least its own prefix, and you can tell which keys belong to what.
Why the separation matters
Flushing the cache does not respect the cache prefix. Cache::flush(), and php artisan cache:clear with it, empties the store. If sessions and queued jobs share that database, clearing the cache signs out every user and deletes pending work. A memory limit with an eviction policy has the same problem: Redis evicts to make room and does not know that a queued job matters more than a cached count.
What a restart costs
Redis holds data in memory. With no persistence configured, a restart leaves an empty server. A cold cache is harmless, because the next request rebuilds it. An empty session store signs everyone out. An empty queue loses jobs that were waiting. If that is unacceptable, keep sessions and the queue on a persistent instance, or in the database, and let the volatile Redis hold only the cache.
Example
<?php
// config/database.php
use Illuminate\Support\Str;
return [
// ... connections, migrations and the rest of the file ...
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'),
],
// Queued jobs live here. This database must not evict keys.
'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'),
],
// The cache store. Safe to flush, safe to evict.
'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'),
],
// A third connection you add yourself, for sessions.
'sessions' => [
'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_SESSION_DB', '2'),
],
],
];When to use it
- Move cache, sessions and the queue off the database onto one Redis server without mixing their keys.
- Explain why clearing the cache signed every user out.
- Decide what may live on an instance that can restart or evict keys.
- Point sessions at a persistent Redis and the cache at a volatile one.
More examples
The .env that wires all three
Three drivers, three database numbers, one server. Written out like this the blast radius of each operation is visible: flushing the cache touches database 1 only. Remember that these values are read through config files, so a deployed application needs config:cache re-run after any change here.
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
# Cache on database 1, through the 'cache' connection.
CACHE_STORE=redis
REDIS_CACHE_DB=1
# Sessions on database 2, through the 'sessions' connection you added.
SESSION_DRIVER=redis
SESSION_CONNECTION=sessions
SESSION_LIFETIME=120
REDIS_SESSION_DB=2
# Queue on database 0, through the 'default' connection.
QUEUE_CONNECTION=redis
REDIS_QUEUE_CONNECTION=default
REDIS_QUEUE=defaultAddressing the store, and going past it
Use the Cache facade for key/value work so the code still runs on another driver. Drop to the Redis facade when you want a sorted set, a list or pub/sub. Mixing them in one key space is where confusing misses come from, because the two prefixes differ.
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;
// The cache API on the redis store.
Cache::store('redis')->put('home.featured', $ids, 300);
// The same call through the helper.
cache()->store('redis')->put('home.featured', $ids, 300);
// Raw commands on a named connection, for structures the cache has no
// API for. These keys get the Redis options prefix, not the cache
// store's prefix, so Cache::get() will not find them.
Redis::connection('cache')->zadd('leaderboard', 120, 'user:42');What each clear actually deletes
Reach for the narrowest command that fixes the problem. cache:forget and tag flushes leave everything else warm; cache:clear and optimize:clear hand every visitor a cold cache at the same moment, which on a busy site is a load spike of its own.
# The default store, prefix and all.
php artisan cache:clear
# A named store only.
php artisan cache:clear redis
# One key.
php artisan cache:forget home.featured
# One tag group, on a store that supports tags.
php artisan cache:clear --tags=reports
# Config, event, route and view caches AND the default cache store.
php artisan optimize:clear
Discussion