Drivers and Tags
How file, database and Redis stores differ, why tests use the array driver, and what cache tags buy and cost.
Cache::store('redis')->tags(['reports'])->put($key, $value, $seconds)A driver is the backend behind the Cache facade. Which one runs is a line of configuration, and the API you write does not change.
The stores
config/cache.php holds a stores array and a default key read from CACHE_STORE. In Laravel 13 the default is database: values are serialized into a cache table, which ships in the default 0001_01_01_000001_create_cache_table.php migration and can otherwise be generated with php artisan make:cache-table. It needs no extra service and works across several web servers, at the cost of a query per read.
file writes serialized values under storage/framework/cache/data. Fine on one machine, invisible to the next machine behind a load balancer. redis and memcached hold values in memory and are the fastest option; they also support tags and atomic locks. array keeps values in PHP memory for the current process only, so nothing survives the test that wrote it, and null discards every write.
Reach a store by name with Cache::store('redis')->get($key), which is the same call as cache()->store('redis')->get($key).
Tags
Tags group keys so you can drop the group: Cache::tags(['reports'])->put($key, $value, 600), then Cache::tags(['reports'])->flush(). The redis, memcached and array stores support tags; file, database, dynamodb and storage do not and throw a BadMethodCallException. Tags cost bookkeeping on every write, and a flush walks the tag's members instead of deleting one key.
Example
<?php
// config/cache.php
return [
'default' => env('CACHE_STORE', 'database'),
'stores' => [
// Nothing persists past the current process. Used by tests.
'array' => [
'driver' => 'array',
'serialize' => false,
],
// The Laravel 13 default: one row per key in the 'cache' table.
'database' => [
'driver' => 'database',
'connection' => env('DB_CACHE_CONNECTION'),
'table' => env('DB_CACHE_TABLE', 'cache'),
],
// One file per key, on this machine only.
'file' => [
'driver' => 'file',
'path' => storage_path('framework/cache/data'),
],
// In memory, and the store to pick if you want tags or locks.
'redis' => [
'driver' => 'redis',
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
],
// Tries 'database' first and falls back to 'array' if it fails.
'failover' => [
'driver' => 'failover',
'stores' => ['database', 'array'],
],
],
// The shipped file also sets 'prefix' from CACHE_PREFIX.
];When to use it
- Move a single-server app off the file driver when a second web server is added.
- Keep tests from sharing cached state by forcing the array store in phpunit.xml.
- Invalidate every cached fragment for one tenant or user in one call.
- Decide whether a feature's design can stay on the default database store.
More examples
Tagging everything cached for one user
This is what tags are for: invalidating a group whose membership you did not track. It requires the redis or memcached store. On the default database store the first line throws, which is why tag-based designs force a driver decision early.
use Illuminate\Support\Facades\Cache;
$userTag = 'user:'.$user->id;
Cache::tags(['invoices', $userTag])
->put("invoices.{$user->id}.page.1", $page, 600);
// When that user's invoices change, drop every key tagged for them,
// without knowing what the keys were called.
Cache::tags([$userTag])->flush();
// Reading needs the same tags that wrote it.
$page = Cache::tags(['invoices', $userTag])->get("invoices.{$user->id}.page.1");The array store in tests
The array store lives in PHP memory for the duration of one process, so one test cannot see what another cached and nothing has to be cleaned up. It also supports tags, which means a tagged design can still be tested without Redis installed.
<!-- phpunit.xml -->
<php>
<env name="CACHE_STORE" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
</php>Choosing the store per value
Stores are not exclusive. A value that is expensive but not critical belongs on the fast volatile store; a value you would rather not lose belongs on the durable one. Naming the store at the call site also documents that intent for the next reader.
use Illuminate\Support\Facades\Cache;
// Default store (database here): survives a Redis restart.
Cache::put('billing.last-sync', '2026-03-01 09:00:00', 86400);
// Named store, for a hot value that can be rebuilt.
Cache::store('redis')->put('home.featured', $ids, 300);
// Shorthand for the line above.
cache()->store('redis')->put('home.featured', $ids, 300);
Discussion