Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Redis Caching: Speed Up Your Laravel/PHP App

By Kokil Thapa | Last reviewed: September 2026

Slow database queries and repeated computation are the bottlenecks I see most often when auditing PHP applications. Redis caching: Speed Up Your Laravel/PHP App is not a package install—it is an architecture choice about volatility, serialization cost, and invalidation rules. Whether you run a high-traffic store or a legal-tech portal, moving from file-based caching to Redis 8.10 is often the highest-ROI infrastructure change you can make. For broader performance work, see our speed optimization services in Nepal.

Before you change drivers, decide what freshness your users require. Real-time booking flows need tighter invalidation than a category listing page. If you are building APIs, pair this guide with Laravel API best practices so cached responses stay consistent with your contract. A Laravel developer in Nepal working on multi-server setups should also plan session and queue separation from day one.

How do you configure Redis as the primary cache driver in Laravel 13?

Laravel 13.x ships with first-class Redis support, but production needs tuned connections, serialization, and prefixing. PHP 8.3 is the minimum for Laravel 13; PHP 8.5 is the current anchor version and what I target on new Ubuntu 24 servers.

Choosing between PhpRedis and Predis

PhpRedis is a C extension with lower overhead under load. Predis installs via Composer only and is fine for local dev. On every production server I manage, I install PhpRedis through the OS package or PECL.

# Install PhpRedis on Ubuntu 24.04 (PHP 8.5)
sudo apt-get install php8.5-redis

# Verify
php -m | grep redis

# Fallback via PECL
sudo pecl install redis
echo "extension=redis.so" | sudo tee /etc/php/8.5/mods-available/redis.ini
sudo phpenmod redis

Set your environment and confirm Laravel sees Redis:

# .env
CACHE_STORE=redis
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_CACHE_DB=1
SESSION_DRIVER=redis
SESSION_CONNECTION=session
QUEUE_CONNECTION=redis

Separating cache, sessions, and queues

A common mistake is putting cache, sessions, queues, and Pub/Sub on Redis database 0. Running php artisan cache:clear can then wipe sessions or queued jobs. Assign distinct database indices in config/database.php.

// config/database.php
'redis' => [
    'client' => env('REDIS_CLIENT', 'phpredis'),

    'options' => [
        'cluster' => env('REDIS_CLUSTER', 'redis'),
        'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
        'persistent' => env('REDIS_PERSISTENT', true),
    ],

    'default' => [
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD'),
        'port' => env('REDIS_PORT', '6379'),
        'database' => env('REDIS_DB', '0'),
    ],

    'cache' => [
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD'),
        'port' => env('REDIS_PORT', '6379'),
        'database' => env('REDIS_CACHE_DB', '1'),
    ],

    'session' => [
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD'),
        'port' => env('REDIS_PORT', '6379'),
        'database' => env('REDIS_SESSION_DB', '2'),
    ],
],

The official Laravel cache documentation covers driver configuration. For Redis server options, see the Redis documentation.

Redis DB SeparationLaravel 13PHP 8.5 + FPMDB 0QueuesDB 1CacheDB 2SessionsNever Mixcache:clear must notdrop jobs or sessionsRedis 8.10 in-memory store
Redis caching for Laravel works best when cache, sessions, and queues use separate database indices

What are the most effective caching patterns for Eloquent queries?

Caching full Eloquent models is often wasteful. Serialized models carry hidden fields, casts, and relationship metadata that bloat memory. Cache the shape your view or API actually needs. This cuts payload size and makes invalidation predictable.

Repository layer with tagged caching

Tags enable group invalidation. Without them, clearing all posts in category X means guessing every key. Redis supports tags natively; Memcached does not. Keep Cache::remember() out of controllers—wrap queries in a repository or service. For a deeper pattern write-up, see Laravel repository pattern explained with real code.

class ProductRepository
{
    public function getActiveProducts(int $categoryId): Collection
    {
        $cacheKey = "products:category:{$categoryId}:active";

        return Cache::tags(['products', "category:{$categoryId}"])
            ->remember($cacheKey, now()->addHours(6), function () use ($categoryId) {
                return Product::where('category_id', $categoryId)
                    ->where('is_active', true)
                    ->select(['id', 'name', 'price', 'slug', 'thumbnail'])
                    ->orderByDesc('updated_at')
                    ->limit(50)
                    ->get();
            });
    }

    public function invalidateCategory(int $categoryId): void
    {
        Cache::tags(["category:{$categoryId}"])->flush();
    }
}

Compare tag behaviour in Laravel cache tags with Redis vs Memcached. On catalog-heavy stores like Nepal Gift Card, tagged category caches pay off quickly.

Stopping the thundering herd with atomic locks

When a hot key expires, many requests may hit MySQL at once. That thundering herd can stall your database. Use atomic locks so one worker regenerates while others wait or serve stale data.

$key = "products:category:{$categoryId}:active";

return Cache::lock("lock:{$key}", 30)->block(5, function () use ($key, $categoryId) {
    return Cache::remember($key, now()->addHours(6), function () use ($categoryId) {
        return Product::where('category_id', $categoryId)
            ->where('is_active', true)
            ->get();
    });
});

Pair this with query tuning from MySQL query optimization for high-traffic apps. Even perfect caching cannot fix an N+1 query inside the regeneration callback.

Atomic Lock FlowRequest ARequest BRequest CRedis LockOne winnerMySQLOne queryCacheFresh writeBlocked requests wait or read stale cache
Atomic locks during Redis cache regeneration prevent simultaneous database hits

How does Redis compare to file and database caching for PHP applications?

File caching works for low-traffic brochure sites. Database caching adds query latency on every miss. Redis wins when read volume is high, data changes slowly relative to reads, or you need sub-millisecond session and API response times.

CriteriaFile CacheDatabase CacheRedis Cache
Read latency5–50 ms (disk I/O)10–100 ms (query + network)<1 ms (in-memory)
ConcurrencyPoor (file locks)Moderate (row locks)Excellent (non-blocking)
Tag supportNoYes (slow)Yes (native sets)
Atomic opsNoLimitedFull (INCR, SETNX, Lua)
Multi-server sessionsUnsafeSlow under loadIdeal
Infra cost (Nepal VPS)ZeroUses existing DBRs 1,500–5,000/mo (~USD 11–37)
Best forDev, static configLow-traffic appsProduction, APIs, eCommerce

For budget-sensitive Nepal projects, I often start on file cache and migrate when monitoring shows miss storms. The jump to Redis typically yields 10–50× throughput on read-heavy endpoints. Operational overhead—monitoring, backups, firewall rules—must be justified by traffic. Read improving web performance with caching strategies for the full picture.

What are the common cache invalidation mistakes in production Laravel apps?

Invalidation bugs erode trust faster than slow pages. Stale inventory, wrong prices, or outdated legal content are hard to debug because the app code looks correct. These mistakes recur on projects I audit, from legal portals to eCommerce platforms.

Missing model observer hooks

If you cache product listings but skip invalidation on update, users see phantom stock. Bind cache clearing to model lifecycle events—not manual controller calls that someone will forget.

class ProductObserver
{
    public function saved(Product $product): void
    {
        Cache::tags(['products', "category:{$product->category_id}"])->flush();
        Cache::tags(['products', "product:{$product->id}"])->flush();
    }

    public function deleted(Product $product): void
    {
        $this->saved($product);
    }
}

Over-caching mutable or user-specific data

Carts, dashboards, and admin panels change every request. Caching them creates ghost state. Reserve Redis for shared reference data: navigation menus, category trees, pricing tiers, feature flags, and aggregated stats. Use database query caching strategies only where the data is truly shared.

Ignoring serialization changes after deploy

Adding a model attribute or changing a cast can break cached serialized objects. After schema-affecting deploys, version your keys or run a targeted flush on the isolated cache DB.

$version = config('app.cache_version', 'v1');
$key = "{$version}:products:category:{$categoryId}";

// deploy.sh — safe because cache uses REDIS_CACHE_DB=1
php artisan config:cache
php artisan cache:clear
Cache This Data?Read vs Write RatioHigh ReadFrequent WritesCache ItMenus, tiersShort TTLSearch listsSkip CacheCart, dashboardQuery OnlyAudit logs
Decision tree for Redis caching shared reference data versus leaving user-specific data uncached

How do you harden and monitor Redis on a production Ubuntu server?

Redis is fast because it holds data in RAM. That same trait makes misconfiguration costly. Bind to localhost or a private network, require a strong password, and cap memory so one cache spike cannot take down the whole VPS.

Baseline redis.conf settings

  1. Set requirepass and store the password only in .env.
  2. Set maxmemory to 70–80% of available RAM after PHP-FPM and MySQL.
  3. Use maxmemory-policy allkeys-lru for pure cache workloads.
  4. Disable dangerous commands: rename-command FLUSHALL "".
  5. Enable AOF or RDB only if you store non-regenerable data in Redis.
# /etc/redis/redis.conf (excerpt)
bind 127.0.0.1 ::1
requirepass your-strong-password
maxmemory 512mb
maxmemory-policy allkeys-lru
rename-command FLUSHALL ""
rename-command FLUSHDB ""

For server setup beyond Redis, see Linux system administration in Nepal and PHP-FPM tuning for high-traffic websites. Queue workers should run under Laravel Horizon when Redis backs your job pipeline.

What to watch in production

Track memory usage, evicted keys, connected clients, and hit rate. A climbing evicted_keys counter means your TTLs or maxmemory are wrong—not that you need a bigger server yet.

redis-cli -a "$REDIS_PASSWORD" INFO memory
redis-cli -a "$REDIS_PASSWORD" INFO stats | grep keyspace
redis-cli -a "$REDIS_PASSWORD" --bigkeys

On booking systems like Adventure Third Pole Trek, I log cache regeneration time alongside queue latency. A slow regeneration callback shows up before users complain. Validate JSON API payloads with our JSON formatter tool when debugging cached API responses.

Cache Hit vs Miss PathHTTP RequestLaravel routeRedis LookupTagged keyCache HitSub-ms responseCache MissLock + queryMySQL 9.7RegenerateFast 200Goal: maximize hit rate on shared read-heavy endpoints
Redis caching request flow showing fast cache hits versus slower miss regeneration from MySQL

PhpRedis connection details and serialization options are documented in the PhpRedis GitHub repository. For broader Redis topology—sentinel, clustering—read Redis persistence and clustering and Redis caching patterns for web apps.

Key Takeaways

  • Install PhpRedis, set CACHE_STORE=redis, and isolate cache, sessions, and queues on separate Redis DB indices.
  • Cache slim data shapes in repositories—not full Eloquent models—and use tags for group invalidation.
  • Apply atomic locks on hot keys so expired cache entries do not trigger a thundering herd on MySQL.
  • Invalidate through model observers; never cache carts, dashboards, or other per-user mutable state.
  • Version cache keys after deploys that change serialization, and monitor Redis memory plus evicted keys.
  • Redis costs Rs 1,500–5,000/month on a Nepal VPS—justify it with measured read latency, not assumptions.

People Also Ask

Is Redis worth it for a small Laravel site in Nepal?

For brochure sites under a few hundred daily visits, file cache is often enough. Redis pays off when you serve APIs, run multiple PHP-FPM workers, need shared sessions across servers, or MySQL slow-query logs show repeated identical reads. Measure first, then upgrade.

Should I use Redis for Laravel sessions and queues too?

Yes on production multi-worker setups. Redis sessions survive horizontal scaling and deploys better than file sessions. Queues on the same Redis instance are fine—just use a separate database index so cache:clear never drops pending jobs.

What TTL should I set for cached Eloquent queries?

Match TTL to business tolerance for staleness. Category menus might live six to twenty-four hours. Product listings often need fifteen minutes to one hour with observer-based invalidation on write. Never use infinite TTL without an explicit invalidation path.

Does Redis caching help Core Web Vitals and SEO?

Faster server response time reduces TTFB, which feeds into LCP on dynamic pages. Redis alone will not fix unoptimized images or bloated JavaScript. Pair caching with guidance from Core Web Vitals optimization and how website speed impacts SEO in Nepal.

Ship faster pages with Redis caching done right

Redis caching to speed up your Laravel/PHP app is one of the most reliable wins I deploy on production systems—but only with clear invalidation rules, isolated databases, and locks on hot keys. Start with your slowest shared read endpoints, measure hit rate for a week, then expand. Need help auditing cache architecture on an existing app? Contact us or explore testing and optimization services to turn latency data into a concrete rollout plan.

Frequently Asked Questions

Redis is an in-memory key-value store used as a high-speed cache driver for Laravel applications.

Self-hosted on a VPS costs Rs 1,500–3,000 monthly (~USD 11–22), while managed services range USD 15–30 monthly.

Use Redis when cache read/write frequency exceeds 100 operations per second or sub-millisecond latency is required.

Set CACHE_STORE=redis in your .env file and ensure the redis connection is defined in config/database.php. Laravel 12 uses this environment variable instead of the deprecated CACHE_DRIVER. Verify connectivity by running php artisan tinker and executing Cache::put('test', true, 60) followed by Cache::get('test'). On Ubuntu servers, confirm the redis-server service is active via systemctl status redis-server before testing application connectivity.

In my experience with production Laravel applications, Redis generally outperforms Memcached for session storage because it supports data persistence, complex data types, and atomic operations. Memcached may show marginally better raw throughput for simple key-value gets, but Redis offers superior reliability during restarts and supports tagged caching natively through Laravel's cache facade. For legal-tech portals handling sensitive client sessions, Redis persistence prevents users from being logged out after server maintenance windows.

Install the php-redis extension via PECL or your package manager. On Ubuntu 24.04 with PHP 8.4, run sudo apt install php8.4-redis then restart PHP-FPM with sudo systemctl restart php8.4-fpm. Verify installation by checking phpinfo() output or running php -m | grep redis. The Predis pure-PHP library works without extensions but adds 2-3ms overhead per operation, which compounds under load. Always prefer the C extension for production environments serving real traffic.

Use Laravel's Cache::lock() method to implement mutex locks around expensive cache regeneration. When multiple requests hit an expired cache key simultaneously, only one process regenerates the value while others wait or return stale data. On a travel booking platform I built, this prevented database overload when popular trekking itinerary caches expired during peak browsing hours. Combine with Cache::rememberForever() for critical reference data and use probabilistic early expiration to spread regeneration across time rather than clustering at exact TTL boundaries.

Yes, Redis serves as both cache store and queue backend in Laravel. Configure separate databases by setting REDIS_CACHE_DB=0 and REDIS_QUEUE_DB=1 in your .env to isolate workloads. This prevents queue workers consuming memory needed for hot cache keys. On eCommerce projects like Nepal Gift Card, I run sessions on database 0, cache on database 1, and queues on database 2. Monitor each database independently using redis-cli INFO keyspace to detect memory pressure before it causes cache evictions or failed jobs.

With default volatile-lru eviction policy, all cached data persists across restarts if RDB snapshots or AOF logging are enabled. Without persistence configured, the entire cache clears on restart, causing immediate database load spikes. In production deployments on Ubuntu servers, I configure appendonly yes in redis.conf for AOF persistence with auto-aof-rewrite-percentage 100. For non-critical cache layers like rendered HTML fragments, accepting data loss on restart reduces disk I/O. Always test failure scenarios during staging validation before deploying persistence configuration changes to live environments.

Run redis-cli INFO memory to check used_memory_human and mem_fragmentation_ratio values. Set maxmemory in redis.conf to prevent unbounded growth, typically 70% of available RAM on dedicated instances. Configure maxmemory-policy allkeys-lru for cache-only instances so Redis evicts least-recently-used keys automatically. On shared EC2 infrastructure hosting multiple sister sites, I set up Prometheus redis_exporter with Grafana dashboards tracking memory trends over 30 days. Alert when fragmentation ratio exceeds 1.5 or used memory approaches 85% of maxmemory threshold.

Cache tags require the php-redis extension; Predis does not support them efficiently. Verify your connection uses the phpredis driver in config/database.php by checking 'driver' => 'phpredis'. Tags create additional Redis sets for each tag, increasing memory overhead proportionally to tag cardinality. On directory sites with hundreds of category tags, I observed 40% higher memory consumption versus untagged caching. Consider structured key naming conventions like users:{id}:profile instead of tags when cardinality exceeds 50 unique tags to reduce set operations and simplify manual cache invalidation debugging.

Bind Redis to 127.0.0.1 in redis.conf unless remote access is absolutely necessary. Enable requirepass with a strong 32-character minimum password stored in Laravel's .env file. Configure UFW to block port 6379 from external networks with sudo ufw deny 6379. Disable dangerous commands like FLUSHALL and CONFIG via rename-command directives in redis.conf. On legal-tech portals storing sensitive document metadata in cache, I additionally enable TLS encryption between Laravel and Redis when they reside on separate hosts. Never expose Redis directly to the internet without authentication and network-level restrictions.

Common causes include PHP-FPM worker exhaustion, Redis maxclients limit reached, or network latency between application and cache servers. Check Redis logs for rejected connections and slowlog get 10 for blocking commands. After Deployer 7 releases, stale PHP-FPM processes sometimes retain old Redis connections; always run sudo systemctl reload php8.4-fpm post-deploy. On one production system, timeout errors traced to opcache holding cached Redis configuration from previous release; adding opcache_reset() to deploy script resolved intermittent failures. Monitor connection pool metrics alongside application error rates during deployment windows.

Self-hosting on a Kathmandu or Singapore VPS provides lower latency for Nepal users and avoids cross-border payment complications. Redis Cloud offers automated backups and scaling but bills in USD with minimum tiers around USD 30 monthly. For projects billing clients in NPR, self-hosted Redis on a Rs 2,500/month VPS keeps infrastructure costs predictable. I self-host for most Nepal legal-tech and eCommerce projects, reserving managed services for clients requiring 99.9% SLA guarantees or multi-region failover. Factor in your team's operational capacity; managing Redis requires monitoring, backup verification, and upgrade planning that managed services handle automatically.

Create an Artisan command that runs Cache::put() and Cache::get() operations 10,000 times with microtime(true) measurements. Test both drivers using identical payloads representing your actual cached data structures. File cache often wins for small, infrequently-accessed data due to OS page caching, while Redis dominates for high-concurrency workloads exceeding 50 concurrent requests. On a florist eCommerce site, Redis reduced average page render time from 180ms to 45ms during flash sales with 200+ simultaneous users. Always benchmark with production-representative data sizes and concurrency levels rather than synthetic microbenchmarks that mislead architectural decisions.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: