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_formandcache_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_binsis 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-editinglocal.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_prefixfor each site to the site's main domain (itsSERVER_NAME) plus a fixed suffix, soexample.com's entries andother-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 = TRUEin the site's control file, run the failing task again, then set it back toFALSEand 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.netandshop-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:
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. |
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(defaultTRUE) is the master switch. With it on, BOA sets the module'sredis_flush_modeto1site-wide and to2for 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_enabledon 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 oncache_clear_all()in all three forms (single key, prefix wildcard, whole bin) on any bin, custom bins included. redis_scan_enable(defaultFALSE) 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, defaultTRUE). - On recovery, BOA clears the local APCu tier (
redis_flush_apcu_on_recovery, defaultTRUE) 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 |
Related
- The front cache & Drupal's page cache — the layers in front of this one.
- PHP opcache & APCu — the per-worker tier on Drupal 8+, and the self-service way to flush it.
- Platform and site INI settings — how to create and edit the control files these settings live in.
- Drush basics — clearing caches from your shell.