How to use Redis for caching a Drupal 8 site

Goal

Drupal 8 functions best with a non-database cache, and particularly well with Redis. This guide shows how to set up a Drupal 8 site to use Redis caching.

Assumptions

  • You already have a working Drupal 8 site on Platform.sh, built with Composer.
  • The project’s plan size has room for an additional Redis service.

A Standard plan should have room for a Drupal 8 site with Redis and one search server, although if the site has a lot of authenticated traffic it may be a good idea to increase it to a Medium plan to allow for more resources for each container. Add Redis first, then benchmark the site to determine if that’s necessary.

Problems

Drupal 8 contains a bug that causes it to fail if the cache backend changes between installation (which always uses the database) and the first page load (which uses the configured cache). Therefore Redis caching cannot be configured out of the box. It can and should be enabled immediately after installation, however.

Steps

1. Add a Redis service

Add the following to the .platform/services.yaml file to create a Redis service in the application:

rediscache:
    type: redis:5.0

That will create a service named rediscache, of type redis, specifically version 5.0.

2. Expose the Redis service to your application

In the .platform.app.yaml file under the relationships section, add the following:

relationships:
    redis: "rediscache:redis"

3. Add the Redis PHP extension

In the .platform.app.yaml file, add the following right after the type block:

# Additional extensions
runtime:
    extensions:
        - redis

4. Add the Drupal module

Add the Drupal Redis module. If using Composer (recommended), a single command will add it:

composer require drupal/redis

Then commit the resulting changes to the composer.json and composer.lock files.

The Redis module may be enabled after the setup below is completed, but should not be enabled until it is.

5. Configure Drupal

Place the following at the end of settings.platformsh.php. Note the inline comments, which allow for further customization. Also review the README.txt file that comes with the redis module, as it has a great deal more information on possible configuration options.

The example below is intended as a “most common case”, so may need adjustment to be optimal for a particular site.

note

If you do not already have the Platform.sh Config Reader library installed and referenced at the top of the file, you will need to install it with composer require platformsh/config-reader and then add the following code before the block below:

$platformsh = new \Platformsh\ConfigReader\Config();
if (!$platformsh->inRuntime()) {
  return;
}
// Set redis configuration.
if ($platformsh->hasRelationship('redis') && !drupal_installation_attempted() && extension_loaded('redis')) {
  $redis = $platformsh->credentials('redis');

  // Set Redis as the default backend for any cache bin not otherwise specified.
  $settings['cache']['default'] = 'cache.backend.redis';
  $settings['redis.connection']['host'] = $redis['host'];
  $settings['redis.connection']['port'] = $redis['port'];

  // Apply changes to the container configuration to better leverage Redis.
  // This includes using Redis for the lock and flood control systems, as well
  // as the cache tag checksum. Alternatively, copy the contents of that file
  // to your project-specific services.yml file, modify as appropriate, and
  // remove this line.
  $settings['container_yamls'][] = 'modules/contrib/redis/example.services.yml';

  // Allow the services to work before the Redis module itself is enabled.
  $settings['container_yamls'][] = 'modules/contrib/redis/redis.services.yml';

  // Manually add the classloader path, this is required for the container cache bin definition below
  // and allows to use it without the redis module being enabled.
  $class_loader->addPsr4('Drupal\\redis\\', 'modules/contrib/redis/src');

  // Use redis for container cache.
  // The container cache is used to load the container definition itself, and
  // thus any configuration stored in the container itself is not available
  // yet. These lines force the container cache to use Redis rather than the
  // default SQL cache.
  $settings['bootstrap_container_definition'] = [
    'parameters' => [],
    'services' => [
      'redis.factory' => [
        'class' => 'Drupal\redis\ClientFactory',
      ],
      'cache.backend.redis' => [
        'class' => 'Drupal\redis\Cache\CacheBackendFactory',
        'arguments' => ['@redis.factory', '@cache_tags_provider.container', '@serialization.phpserialize'],
      ],
      'cache.container' => [
        'class' => '\Drupal\redis\Cache\PhpRedis',
        'factory' => ['@cache.backend.redis', 'get'],
        'arguments' => ['container'],
      ],
      'cache_tags_provider.container' => [
        'class' => 'Drupal\redis\Cache\RedisCacheTagsChecksum',
        'arguments' => ['@redis.factory'],
      ],
      'serialization.phpserialize' => [
        'class' => 'Drupal\Component\Serialization\PhpSerialize',
      ],
    ],
  ];
}

The example.services.yml file noted above will also use Redis for the lock and flood control systems.

6. Deploy and Verify Redis

Commit the changes to settings.platformsh.php, composer.*, .platform.app.yaml, and .platform/services.yaml. Then git push.

Once the deploy is finished, run:

platform ssh "redis-cli -h redis.internal info"

That will show the Redis status information. Toward the top of the output is the memory allocation. If that amount changes as the site is used it means Drupal is correctly connecting to Redis and writing cache values to it.

7. Clear SQL cache tables

Once Redis is confirmed running and in use, clear out the remaining, vestigial values in the SQL database cache as they are no longer valid. To do that, connect to the database:

platform sql

And run TRUNCATE cache_* for every table that begins with cache_, except for cache_form. Despite its name cache_form is not part of the cache system proper and thus should not be moved out of SQL.

To see all the cache tables, run:

show tables like 'cache_%';

That will list tables like cache_bootstrap, cache_config, cache_container, etc. Then run:

TRUNCATE cache_bootstrap;
TRUNCATE cache_config;
TRUNCATE cache_container;

And so on.

Conclusion

Using Redis for Drupal caching can improve performance, and more importantly free up valuable database disk space. That helps avoid out-of-disk lockups on the database that can impact site availability.

Thanks @Crell very helpful
The setup for Redis with Drupal 8 works all fine. My setup uses ephimeral redis 6.0.1 with no eviction policy. I have few queries on how it works

  1. The Drupal module REDIS is disabled in CMS but the redis info displays different memory allocations each time. Does the module need to be enabled?
  2. My setup also replaces default FLOOD table with redis. Drupal 8 has a system cron job (CLEANUP) which executes every 15 minutes to clean caches, batch, flood, temp-files, etc. Does this mean it is worthless to use it for cache and flood clean up?
  3. To clean up flood table, what is the correct process? Below is the list of keys that include flood
    (128) “drupal.redis.8.9.x.x.x:flood:user.failed_login_user:{user}-{ip}”
    (935) “drupal.redis.8.9.x.x.x:config:user.flood”
    (3776) “drupal.redis.8.9.x.x.x:flood:user.failed_login_ip:{ip}”