Skip to content

Grav Caching Deep Dive — Cache Types, Warming and Cache Busting

DodaTech Updated 2026-06-27 7 min read

In this tutorial, you'll learn Grav caching in depth — understanding cache types (file, Redis, Memcached), cache warming strategies, cache busting techniques, and optimizing cache configuration for maximum performance.

What You'll Learn

  • Grav's caching architecture and cache types
  • File-based caching vs Redis vs Memcached
  • Cache warming: pre-rendering pages to cache
  • Cache busting: forcing cache refresh when content changes
  • Cache configuration for production
  • Monitoring and debugging cache performance

Why It Matters

In WordPress, caching requires plugins like W3 Total Cache or server-level solutions. In Grav, caching is built into the core and configurable at multiple levels. Understanding cache types, expiration strategies, and warming techniques is the difference between a site that loads in 200ms and one that loads in 2 seconds. Proper caching is the single highest-impact performance optimization you can make.

Real-World Use

A documentation site with 5,000 pages was taking 4-6 seconds per page load. After enabling Redis caching with a 1-hour TTL and implementing cache warming (pre-caching all pages after every deployment), the average load time dropped to 80ms. The server CPU usage dropped from 80% to 15%. The improvement came entirely from proper cache configuration — no code changes.

Learning Path

flowchart LR
    A["E-commerce with Grav"] --> B["Caching Deep Dive
← You are here"]:::current B --> C["Performance Optimization"] C --> D["Security"] D --> E["Git Workflow"] E --> F["CLI Tools"] F --> G["Production Deployment"] classDef current fill:#38bdf8,color:#0f172a,stroke-width:2px

Cache Types

Grav supports three cache backends:

Type Storage Speed Best For
File Disk Medium Development, low-traffic sites
Redis Memory Fast High-traffic, multi-server setups
Memcached Memory Fast Distributed caching, large sites

File Cache (Default)

Configured in user/config/system.yaml:

cache:
    enabled: true
    driver: auto  # auto = file-based by default
    prefix: 'grav'
    lifetime: 604800  # 7 days in seconds
    gzip: true
    clear_images_by_default: true

Redis Cache

cache:
    enabled: true
    driver: redis
    prefix: 'grav'
    lifetime: 604800
    redis:
        socket: false
        server: 127.0.0.1
        port: 6379
        password: ''  # Optional
        database: 0

Install Redis:

sudo apt install redis-server php-redis

Memcached

cache:
    enabled: true
    driver: memcache
    lifetime: 604800
    memcache:
        server: 127.0.0.1
        port: 11211

Cache Layers

Grav caches at multiple levels:

Twig Cache

Compiled Twig templates are cached as PHP files:

twig:
    cache: true
    cache_path: 'cache://twig'

Page Cache

Full page output is cached:

cache:
    enabled: true
    check:
        pages: true
        yaml: true
        twig: true

Asset Cache

Merged and minified CSS/JS are cached:

assets:
    css_pipeline: true
    css_minify: true
    js_pipeline: true
    js_minify: true

Cache Warming

Warm the cache by visiting all pages (pre-render and cache them):

user/plugins/cache-warmup/cli/WarmupCommand.php:

<?php
namespace Grav\Plugin\Console;

use Grav\Console\ConsoleCommand;
use Symfony\Component\Console\Input\InputOption;

class WarmupCommand extends ConsoleCommand
{
    protected function configure()
    {
        $this
            ->setName('cache:warmup')
            ->setDescription('Warm up the cache by visiting all pages')
            ->addOption(
                'concurrency',
                'c',
                InputOption::VALUE_OPTIONAL,
                'Number of concurrent requests',
                5
            );
    }

    protected function serve($input, $output)
    {
        $this->setupConsole($input, $output);
        $io = $this->getIO();
        $concurrency = (int)$input->getOption('concurrency');

        $pages = $this->grav['pages']->all();
        $io->title('Cache Warming');
        $io->writeln('Found ' . count($pages) . ' pages to warm');

        $io->progressStart(count($pages));
        $base = $this->grav['uri']->base();

        foreach ($pages as $page) {
            if ($page->published()) {
                $url = $base . $page->route();
                $ch = curl_init($url);
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                curl_setopt($ch, CURLOPT_TIMEOUT, 10);
                curl_exec($ch);
                curl_close($ch);
                $io->progressAdvance();
            }
        }

        $io->progressFinish();
        $io->success('Cache warmed for ' . count($pages) . ' pages');
    }
}

Run: bin/grav cache:warmup

Cache Busting

Force cache refresh when content changes:

cache:
    check:
        pages: true   # Check if page modified time changed
        yaml: true    # Check if YAML config changed
        twig: true    # Check if Twig templates changed

Manual Cache Clear

# Clear all caches
bin/grav cache --clear

# Clear specific cache
bin/grav cache --clear-twig
bin/grav cache --clear-images
bin/grav cache --clear-assets

# Via PHP code
$this->grav['cache']->clearCache('all');
$this->grav['cache']->clearCache('twig');
$this->grav['cache']->clearCache('images');

Automatic Cache Busting

public function onAdminAfterSave()
{
    // Clear cache when pages are saved
    $this->grav['cache']->clearCache('standard');
}

Cache Configuration for Production

user/config/system.yaml:

cache:
    enabled: true
    driver: redis
    prefix: 'grav'
    lifetime: 604800
    gzip: true
    allow_url_fopen: false
    check:
        pages: true
        yaml: false      # Disable in production
        twig: false      # Disable in production
    redis:
        server: 127.0.0.1
        port: 6379

twig:
    cache: true
    auto_reload: false
    autoescape: true

assets:
    css_pipeline: true
    css_minify: true
    js_pipeline: true
    js_minify: true
    enable_asset_timestamp: true
    collections:
        jquery: 'system://assets/jquery/jquery.min.js'

Monitoring Cache Performance

// Add to base template for debugging
{% if grav.user.authorize('admin.super') %}
    <!-- Cache status -->
    Cache hit: {{ grav.cache.getHitCount() }}
    Cache miss: {{ grav.cache.getMissCount() }}
    Cache size: {{ grav.cache.getSize() }}
{% endif %}

Learning Path

flowchart LR
    A["E-commerce with Grav"] --> B["Caching Deep Dive
← You are here"]:::current B --> C["Performance Optimization"] C --> D["Security"] D --> E["Git Workflow"] E --> F["CLI Tools"] classDef current fill:#38bdf8,color:#0f172a,stroke-width:2px

Common Mistakes

  1. Not disabling check.yaml and check.twig in production: Every check requires filesystem stats. In production, disable checks for YAML and Twig to eliminate unnecessary overhead.

  2. Using file cache for high-traffic sites: File cache works by reading/writing PHP files. For more than 10,000 requests/day, switch to Redis or Memcached for better performance.

  3. Not warming cache after deployment: The first Visitor after deployment hits cold cache and gets a slow page. Always warm the cache after code or content changes.

  4. Setting too short a cache lifetime: A 1-hour cache lifetime means every page is regenerated 24 times per day. Use 7 days (604800 seconds) and clear cache manually when content changes.

  5. Forgetting to clear image cache after theme changes: Image manipulations are cached. If you change image styles or dimensions, clear the image cache to regenerate all images.

Practice Questions

  1. What are the three cache drivers available in Grav? Answer: File (disk), Redis (memory), and Memcached (memory). File is the default and works for low-traffic sites. Redis and Memcached are recommended for production.

  2. Why should you disable check.yaml and check.twig in production? Answer: Each check requires Grav to stat files to see if they changed. Disabling these checks eliminates unnecessary filesystem operations, improving performance.

  3. How do you warm the cache for all pages? Answer: Use a CLI command (bin/grav cache:warmup) that visits every published page via HTTP requests, forcing Grav to render and cache each one.

  4. What is the recommended cache lifetime for production? Answer: 604800 seconds (7 days). This long TTL means pages are rarely regenerated. Clear the cache manually when content changes.

  5. Challenge: Set up a complete caching Strategy for a production Grav site. Install Redis and configure it as the cache driver. Configure production-optimized cache settings (disable YAML and Twig checks, 7-day lifetime). Create a cache warming CLI command. Set up automatic cache clearing on page saves. Verify cache hits by monitoring page load times before and after warming. Compare performance with file cache vs Redis. Document the setup for the operations team.

FAQ

What is the difference between `cache.driver: auto` and `cache.driver: file`?

auto tries to use the best available driver (Redis > Memcached > file). file explicitly uses file-based caching. Use auto for development and explicit driver for production.

How does Grav know when to invalidate cache?

Grav checks the modification time of page files, YAML config, and Twig templates (when enabled). If any source file changed since the cache was created, the cache is invalidated.

Does Grav cache work with server-level caching (Varnish, Nginx)?

Yes. Grav sets appropriate cache headers. You can use Varnish or Nginx FastCGI cache in front of Grav for additional performance. Grav's cache handles the application layer.

How do I clear cache programmatically from a plugin?

Use $this->grav['cache']->clearCache('all') to clear all caches, or specify a type: 'standard', 'twig', 'images', 'assets', 'plugins'.

What is the impact of gzip compression on cache?

cache.gzip: true compresses cached pages with gzip, saving disk space and reducing I/O. The trade-off is slightly higher CPU usage for compression/decompression.

Mini Project

Goal: Build and optimize a complete caching system for a Grav site.

  1. Install Redis and configure Grav to use it
  2. Configure production cache settings (7-day lifetime, disable checks)
  3. Create a cache warming plugin with a CLI command
  4. Add automatic cache clearing on page save events
  5. Create a cache monitoring dashboard widget
  6. Benchmark page load times: no cache, file cache, Redis cache
  7. Test cache invalidation when pages are modified
  8. Set up image caching and test regeneration on theme changes
  9. Configure asset pipeline caching with timestamps
  10. Document the caching architecture and procedures

What's Next

Now you understand caching. Next, learn performance optimization:

Continue to Lesson 36: Performance Optimization — Page speed, CDN, image optimization, and Performance Testing.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro