Not all cached data is alike. A rendered page fragment, a piece of discovered plugin metadata, and a small application-level counter all get cached — but they're read and written at wildly different rates and sizes. Drupal keeps them apart in separate cache bins specifically so you can tune, monitor, or clear one category without touching the others.
What you'll learn in this lesson
- What a cache bin actually is under the hood
- The bins you'll encounter most often, and what each one holds
- How to target a specific bin from your own code
The bins you'll meet constantly
cache.default(bin namedefault) — the general-purpose bin most custom module code writes to when it doesn't specify otherwise.cache.render— cached render arrays and page fragments; this is the bin most affected by the tags/contexts/max-age metadata from earlier lessons.cache.data— general structured data your code decides to cache, distinct from render output.cache.discovery— plugin definitions, hook implementations, and other "what exists on this site" metadata, rebuilt ondrush cr.cache.bootstrap— data needed extremely early in the request lifecycle, before most of Drupal has even loaded.
Using a specific bin from your own code
// Inject the specific bin as a service, e.g. in a form's create():
$form->cacheBackend = $container->get('cache.default');
// Then use it exactly like any other cache backend:
$this->cacheBackend->set('my_module_expensive_result', $data, CacheBackendInterface::CACHE_PERMANENT);
$cached = $this->cacheBackend->get('my_module_expensive_result');
This is the exact pattern demonstrated in the Examples module's cache_example — a form injects cache.default as a service and uses the ordinary get()/set() cache-aside pattern against it. If you had a good reason to keep a particular module's cached data physically separate (heavy write volume, a need to clear it independently, or database-level tuning specific to that workload), you could define your own custom bin in a *.services.yml file instead of reusing cache.default.
cache.default unless you have a concrete, measured reason a specific workload needs isolation.Quick check: after `drush cr`, your custom module's expensively-computed cached result is gone, even though you thought it was permanent. What likely happened? (A full `drush cr` clears every bin, including `cache.default` — "permanent" in the Cache API means "doesn't expire on its own," not "survives a full cache rebuild." Only tag-based invalidation and time-based expiry are things your code controls; a full rebuild clears everything regardless.)
Key takeaways
- Cache bins separate different categories of cached data (render, discovery, bootstrap, general-purpose) so each can be reasoned about and tuned independently.
- Most custom code should use
cache.defaultunless there's a concrete, measured reason to isolate a workload into its own bin. CACHE_PERMANENTmeans "no automatic time-based expiry," not "immune to a full cache rebuild" —drush crclears every bin.
Coming up next
To close out this section, we look at entity.lazy_builder — a mechanism for deferring entity rendering work until it's actually needed, rounding out the toolkit alongside BigPipe and cache bins.