Keep Laravel Redis Counters Compatible with INCR

Redis atomic counters must be stored as plain integer strings. If PhpRedis serializes or compresses a zero before Laravel calls INCR, Redis cannot parse the stored bytes as an integer and the counter may return failure instead of advancing.

Last updated: November 5, 2026.

<?php
// config/database.php
return [
    'redis' => [
        'client' => 'phpredis',
        'options' => [
            'serializer' => Redis::SERIALIZER_NONE,
            'compression' => Redis::COMPRESSION_NONE,
            'prefix' => env('REDIS_PREFIX', 'app_'),
        ],
        'cache' => [
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'database' => env('REDIS_CACHE_DB', 1),
        ],
    ],
];

Use this raw configuration for a Redis client that backs counters. If other values require serialization or compression, isolate them behind a separate client or service instead of sharing one incompatible option set.

Why the counter fails

Laravel documents the available PhpRedis serialization and compression options. Their defaults are SERIALIZER_NONE and COMPRESSION_NONE. The Redis INCR command requires the existing value to be representable as a base-10 64-bit integer; serialized or compressed bytes do not satisfy that contract.

A current Laravel framework report describes this interaction with queue exception counters: an initial cached zero is transformed, the following increment fails, and maxExceptions may not stop the job as expected. The same design risk applies to any limiter or lock implementation that assumes raw numeric Redis values.

Test the real production path

In a staging environment with the production Redis options, write zero through the same Laravel cache store, increment it, and assert that the result is exactly one. Then run a queued job that always throws and verify it fails after its configured exception limit. Laravel’s queue documentation explains that MaxExceptions limits unhandled exceptions, which differs from releases that consume ordinary attempts.

Monitor jobs that retry beyond their limits and cache increments returning false. After changing Redis options, clear only disposable cache data; never flush shared sessions, queues, or locks without a migration plan.

Keep typed objects in a separate cache design if compression is valuable. Continue with Laravel rate limiter storage, tenant rate limits, and safe Redis key scoping.

Related Web Cheat Sheet guides

Sergey Kornilov

Sergey Kornilov