Skip to content

Powered by Grav

The Redis/Valkey object cache

The Redis/Valkey object cache

Every site on BOA gets a fast, in-memory object cache with zero setup: all of Drupal's cache bins are served from Redis (or Valkey, its drop-in replacement — current servers run Valkey, and everything below applies identically). The database is still there underneath, but on a normal request Drupal never touches it for cache reads.

You don't turn any of this on, and — the question every developer eventually asks — you don't register anything either. This page walks through how the routing works, what's guaranteed, and how to check it, ending with the full table of INI settings that tune it.

One name, two servers. All settings keep the redis_ prefix even on a Valkey server, so nothing you configure changes when your host runs one or the other.

Every bin rides the fast cache — including your custom bins

BOA sets Drupal's default cache class (cache_default_class on Drupal 6/7) to the Redis backend for every site. In Drupal, any cache bin that doesn't have an explicit per-bin override inherits that default — which means a custom bin your own module defines (say, cache_mymodule_content) is served from Redis/Valkey automatically, exactly like cache, cache_page, or cache_views. There is no whitelist, no registration, and nothing to ask your host for. New bins are covered the moment they exist.

The routing is opt-out, and the exceptions are deliberate:

  • cache_form and cache_bootstrap (Drupal 6/7) are kept in the database on purpose — form tokens don't belong in a cache that may evict them.
  • redis_exclude_bins is your own opt-out: name any bins in this INI setting and they're served from the database instead. It's the clean replacement for hand-editing local.settings.php, and it's the only per-bin lever — there is no "include" counterpart because none is needed.
  • On Drupal 8+, the same idea applies with modern names: the default backend is Redis/Valkey, and the three hottest bins (bootstrap, discovery, config) are upgraded to ChainedFast — a per-worker APCu front tier with Redis/Valkey behind it (see PHP opcache & APCu).

Your own local.settings.php is read after BOA's cache wiring, so if you ever need something unusual — one bin on a different backend, say — your file wins. For the common case, prefer redis_exclude_bins: it survives updates and is visible at a glance.

One shared instance, one key prefix per site

Each server runs one Redis/Valkey instance shared by all sites on that box — not one per site. Two things keep that safe and tidy:

  • The instance itself is unreachable from outside. It listens only on the local host, protected by a server-held password. Sites talk to it through BOA-generated settings; neither visitors nor other tenants can connect to it directly.
  • Every site's keys carry a unique prefix. BOA sets Drupal's cache_prefix for each site to the site's main domain (its SERVER_NAME) plus a fixed suffix, so example.com's entries and other-site.net's entries can never collide. The prefix is site-wide: it namespaces every bin, your custom bins included, with no code on your side.

The honest fine print: prefix isolation is a namespacing guarantee, not a security wall between sites — that wall is the fact that only BOA-managed sites can reach the instance at all. And because the instance is shared, sites on a busy server share its memory ceiling too (more under Size and limits below).

Reusing a domain name: stale leftovers survive the site

The prefix scheme has one edge worth knowing before it bites. The prefix is derived from the site's main domain name, and the entries live in the shared in-memory store — not in the site's database. Delete the site's database, or the whole site, and its cache entries don't go with it: they sit in Redis/Valkey until they expire or are evicted — and expiry does less than you'd hope. On Drupal 6/7 the default TTL backstop (described under cache clearing below) sweeps everything within a day; on Drupal 8+, which doesn't use those flush modes, there is no such sweep — leftovers can linger far longer, so don't count on time cleaning up for you.

So when a new site comes up under a recently used name — a delete-and-recreate, a re-import, or a Migrate/Clone dance that lands a fresh copy on the old name — it wakes up with the previous occupant's cache prefix and reads its leftovers. The classic symptoms are confusing precisely because they're transient: fatal errors pointing at module paths that no longer exist (the old site's cached registry), a Migrate task that fails right after the switch, or stale content that no cache clear seems to explain. On Drupal 6/7 there's a bonus head-scratcher: a day later it's all mysteriously fine, because the TTL backstop swept the leftovers overnight.

Two ways to deal with it:

  • The quick fix, when a task just failed: set redis_cache_disable = TRUE in the site's control file, run the failing task again, then set it back to FALSE and finish with one more cache clear so nothing stale can resurface. With the fast cache off, the site can't read the leftovers while the task runs. (On Drupal 6/7 the 24-hour backstop would sweep them within a day anyway; on Drupal 8+ don't count on time alone.)
  • The naming strategy that avoids it entirely: give every real site a unique, permanent local name (say shop-live.example.net and shop-stage.example.net) and attach the public domain only as an alias. The cache prefix follows the main name — Nginx hands Drupal the vhost's primary name no matter which alias the visitor arrived on — so moving the public domain between sites moves the traffic without ever re-entering an old cache namespace. The same habit keeps vhosts unambiguous during clone-and-migrate work; see Cloning and migrating and Aliases and redirects.

A diagnostic aside: on a .dev. preview URL BOA adds an X-Valkey-Prefix response header showing exactly which prefix the site is using — handy when you want to confirm what namespace a site actually resolved to.

Confirming it's actually working

You can verify the cache from the outside, without admin access, using plain curl. Set this in your site or platform INI file:

INI
redis_debug_header = TRUE

and every response gains neutral, non-sensitive X-Cache-* headers:

Header Values How to read it
X-Cache-State up / down / backoff / nophpredis / disabled up means the fast cache served this request's backend. backoff means a recent probe failed and the site is briefly on the database (see the fallback section). disabled means it's switched off by INI.
X-Cache-Backend chainedfast / redis / db Which backend actually handled this request. db on a request means it ran degraded — fine during a blip, a problem if you see it constantly.
X-Cache-Reason ok, connect-tcp-exception, … Why the last probe reached the state it did.
X-Cache-Anon ANONYMOUS / LOGGED Whether the response was for an anonymous or a logged-in session.
BASH
curl -sI https://example.com/ | grep -i x-cache

Turn the setting off again when you're done — it's designed for investigation, not permanent decoration.

Reasoning about one specific bin. The headers describe the site's cache state, not a single bin, so to conclude "my custom bin is on Redis" combine three facts: X-Cache-Backend shows redis (or chainedfast), the bin is not listed in your redis_exclude_bins, and your own local.settings.php doesn't override that bin's class. If all three hold, the bin is on the fast cache — that's what the default routing guarantees. For a definitive live check from inside Drupal (reading the effective cache class for one named bin), open a support request; the command it takes isn't available in the limited shell.

The X-Cache-* family is deliberately the live-URL exception among BOA's debug headers: it's gated only by the INI setting above, while the wider X-* debug family (the X-Ini-* set that reads back your effective INI values and names the file each came from, X-Valkey-Prefix, and friends) appears only on .dev. hostnames.

Three more diagnostic aids worth knowing: appending ?noredis=1 to any URL serves that one request with the object cache off entirely — a quick with/without comparison when you're deciding whether a symptom is cache-related. On a .dev. preview hostname BOA adds X-Ini-... headers showing your active INI values (handy to confirm a setting landed). And the redis_debug INI setting arms a server-side fallback log that your host can use to trace probe events over time — setting it is your side; reading the log is theirs.

How cache clearing works (Drupal 6/7)

Drupal 6/7's Redis integration has a family of flush-behaviour settings, and BOA ships opinionated defaults so that cache clearing just works — including the wildcard form your own module might use, cache_clear_all($prefix, $bin, TRUE):

  • redis_flush_forced_mode (default TRUE) is the master switch. With it on, BOA sets the module's redis_flush_mode to 1 site-wide and to 2 for the volatile bins (cache_page, cache_block, cache_menu, cache_metatag), so flushes physically remove matching entries rather than relying on the module's laziest mode.
  • The same switch caps every "permanent" cache entry at a 24-hour TTL (redis_perm_ttl = 86400). That's a self-healing backstop: even an entry a buggy clear missed is gone within a day, and it's also what keeps every key evictable under memory pressure.
  • Wildcard and prefix clears are executed server-side with EVAL — BOA enables the integration's redis_eval_enabled on every Drupal 6/7 site, so a wildcard delete runs as one fast atomic script instead of a slow key-by-key crawl. You can rely on cache_clear_all() in all three forms (single key, prefix wildcard, whole bin) on any bin, custom bins included.
  • redis_scan_enable (default FALSE) exists as an alternative wildcard mechanism using SCAN. It's off for a reason — it has a history of rare, random cache-consistency problems on Drupal 6/7 — and turning it on also disables cache compression. Leave it alone unless your host asks you to test it.

If you hit the opposite problem — entries expiring sooner than a module expects, classically "random blank pages" — redis_flush_forced_mode = FALSE turns the whole opinionated block off for that site. That's the documented escape hatch, but read Size and limits first: it also removes the 24-hour TTL backstop.

Drupal 8+ doesn't use any of these modes — its Redis module invalidates by cache tags, and BOA leaves that mechanism alone.

Locks ride Redis too ("Fast Redis Lock")

BOA also points Drupal 6/7's lock API at Redis: with redis_lock_enable (default TRUE), core's lock_acquire() / lock_wait() / lock_release() use the Redis lock backend instead of the database — automatically, with no code changes in your module.

This is exactly the tool for the "many visitors trigger the same expensive rebuild at once" problem: have the first request take the lock and build, let the rest lock_wait() briefly and then read the cached result. Because the lock lives in the same fast shared store as the cache, acquiring one costs microseconds, not a database round-trip.

The same pattern powers redis_path_enable (default TRUE), which serves the Drupal 6/7 path-alias cache from Redis as well.

If the fast cache is unavailable (see the next section but one), locking falls back to Drupal's database implementation together with everything else — your code doesn't need to care.

Size and limits — what happens under memory pressure

There is no per-bin or per-site quota: your custom bin can hold as many entries as it likes, and entries of a couple of megabytes each are fine — well below any hard per-value limit. What actually bounds you:

  • The shared memory ceiling. The instance gets a fixed, generous share of the server's RAM, sized by BOA and shared by all sites on the box.
  • Eviction, not errors. With the default 24-hour TTL on every entry (see above), the instance evicts the least-frequently-used entries as it approaches the ceiling. Under pressure your bin quietly loses its coldest entries and rebuilds them on demand — writes keep succeeding, and a well-behaved cache consumer never notices.
  • Compression works in your favour. On Drupal 6/7 BOA uses the compressed cache class by default, so text-heavy multi-megabyte entries (HTML, serialized arrays, JSON) shrink substantially before they're stored.

One edge worth knowing if you cache a lot of permanent data: setting redis_flush_forced_mode = FALSE removes the 24-hour TTL, and entries stored as permanent then have no expiry — which makes them ineligible for eviction. If such entries ever fill the shared ceiling, the instance starts refusing new writes instead of evicting old ones, and that hurts every site on the box. If your design genuinely needs large amounts of never-expiring cached data, talk to your host first.

If the cache goes away: automatic database fallback

Redis/Valkey being briefly unreachable — a restart, a blip — does not take your site down, and doesn't need any preparation in your module:

  • BOA probes the cache backend when building your site's settings on each request. If the probe fails, the whole cache configuration steps aside for that request and every bin — custom bins included — transparently falls back to Drupal's standard database cache. The site runs slower and stays up.
  • A failed probe trips a short circuit breaker (redis_backoff_ttl, default 15 seconds): the site stays on the database for that window instead of re-probing on every request. A single sub-second blip is absorbed even earlier by a built-in retry (redis_probe_retry, default TRUE).
  • On recovery, BOA clears the local APCu tier (redis_flush_apcu_on_recovery, default TRUE) so Drupal 8+ sites don't serve stale front-tier entries written during the degraded window.

You can watch all of this happen in the X-Cache-State / X-Cache-Backend headers described above — backoff and db are the degraded window, up and redis the recovery.

One thing your module should do (Drupal 6/7): define the standard cache table for your custom bin in hook_schema(), as every cache-bin-owning module normally does. The database fallback — and redis_exclude_bins, if you ever use it — serves your bin from that table, so it needs to exist. BOA doesn't create tables for custom bins; that part of the contract is yours.

The redis_* INI settings

All of these work in either control file — site or platform (see Platform and site INI settings for the mechanics). Defaults are safe for everyone; set one only to change it.

Setting What it does Default
redis_exclude_bins Comma-separated list of cache bins to keep in the database instead of Redis/Valkey — the per-bin opt-out. Example: redis_exclude_bins = "cache_form,cache_foo". (none excluded)
redis_cache_disable TRUE turns the fast cache off entirely for the affected sites. Debugging only. FALSE
redis_lock_enable The Redis-backed lock for Drupal 6/7 core's lock_acquire()/lock_wait(). On by default; FALSE returns locking to the database. TRUE
redis_path_enable The Redis-backed path-alias cache for Drupal 6/7. TRUE
redis_flush_forced_mode Drupal 6/7 only. The opinionated flush block: physical wildcard deletes plus the 24-hour TTL cap on permanent entries. Set FALSE only if entries genuinely must not expire early. TRUE
redis_scan_enable Drupal 6/7 only. SCAN-based wildcard deletes instead of EVAL. Known to cause rare random problems; also disables cache compression. Leave off. FALSE
redis_old_nine_mode TRUE only if you run Drupal 9 older than 9.3. FALSE
redis_old_eight_mode TRUE only if you run Drupal 8 older than 8.8. FALSE
redis_connect_timeout Seconds the per-request backend probe waits for a TCP connect before treating the backend as unreachable for this request. 0.7
redis_read_timeout Seconds the probe waits for the AUTH/PING reply before giving up. 0.7
redis_backoff_ttl Seconds to stay on the database cache after a failed probe before probing again (the circuit breaker). 15
redis_probe_retry Retry the probe once before tripping the backoff, so a sub-second blip doesn't cost a whole backoff window. TRUE
redis_flush_apcu_on_recovery Clear the local APCu front tier when the backend comes back up, discarding stale entries from the degraded window. TRUE
redis_debug_header Emit the X-Cache-* diagnostic headers described above. Enable while investigating, then disable. FALSE
redis_debug Arm the server-side probe fallback log (your host creates and reads it; this setting just allows it to work for your sites). FALSE

© 2026 BOA Documentation. All rights reserved.