Runtime Cache usage can rise for three different reasons: more function invocations reached the cache, each invocation performed more reads, or each read returned more data. Compare function invocations, reads per invocation, and entry size to separate those causes. Then find the routes and cache keys behind the usage, and remove reads and writes that do no work.
Copy link to headingUnderstand Runtime Cache usage
The runtime cache is a regional key-value cache shared by Edgenivo Functions, Routing Middleware, and builds. Your code reads from it in two ways:
- Calls to
cache.get()from thegetCachehelper in@vercel/functions, in any framework. - The
use cache: remotedirective in Next.js 16, which requires thecacheComponentsflag and stores the output of a function or component in the runtime cache, reading it back on later requests.
Reads and writes are billed in 8 KB units rounded up for each operation. Reading a 1 KB entry uses one read unit, while reading a 100 KB entry uses 13.
The regional price per million units ranges from $0.40 to $0.64 for reads and $4.00 to $6.40 for writes. Because a write unit costs ten times as much as a read unit, compare cost when both metrics increase.
Useful cache hits can reduce compute and origin requests even when the read count is high. Focus on repeated reads of the same key within a request, unused data, and keys that rarely hit.
The Usage dashboard reports ISR separately from Runtime Cache. Confirm which metric increased before investigating the code.
Next.js App Router apps can also use the Data Cache for fetch calls with force-cache or a positive next.revalidate, and for functions wrapped in unstable_cache. Its activity appears in the Runtime Cache area of Observability.
Data Cache is merging into Runtime Cache. If your app uses it, follow Optimize Data Cache usage before you migrate in step four.
Copy link to headingBefore you begin
Compare the same project and environment across both time windows:
- Select one team and project. On Hobby, projects in a team share Runtime Cache storage. Data Cache storage is shared on Hobby and Pro, so writes from another project can affect eviction.
- Use one environment. Production and preview have separate caches.
- Choose equal-length windows before and after the increase.
Choose both comparison windows within your available Observability history: 12 hours on Hobby, one day on Pro, three days on Enterprise, or 30 days with Observability Plus.
Copy link to heading1. Confirm the usage source
- Open Usage and select the period containing the increase.
- Compare the projected cost of Runtime Cache Reads and Runtime Cache Writes, then group the metrics by project and region to locate the increase.
- Record whether reads rose on their own or alongside writes. Use that pattern in step 3 to narrow the likely cause.
Copy link to heading2. Find the tags and code paths driving reads
Open your project, then go to Observability → Runtime Cache. Until your team migrates, the page opens on Data Cache, so switch to the Runtime Cache view before reading the charts.
The page shows reads, writes, hit rate, and on-demand revalidations, followed by a cache-tag table. Observability Plus adds Reads, Writes, Revalidate, and Unique Entries to the Tag and Hit Rate columns.
- With Observability Plus, sort the tag table by Reads to identify the tags contributing most usage.
- Search your codebase for those tags in
cacheTag(...)calls and thetagsoption ofcache.set(...). Without tag-level read counts, inspect calls to these APIs to identify possible sources.
Divide cache reads by function invocations from Observability → Edgenivo Functions over the same window. Compare the result with the number of entries a request needs to spot repeated or looped reads.
Routing Middleware and builds can add cache reads without adding function invocations. If either reads the cache, the project-wide ratio includes those reads and cannot be treated as an exact per-route count.
The Entries table shows each entry's median size, measured on reads served from the cache, in its Entry Size column. The tag table has no size column, so look up a hot tag's entries there. For an entry with no cached reads yet, estimate the serialized size of the value your code stores, and record byte counts rather than cached values. Use the billing unit definition to translate the size into an estimated read-unit count.
Copy link to heading3. Interpret the pattern
| What you see | Likely cause | Where to look next |
|---|---|---|
| Reads and invocations rose by the same factor | More function invocations reached the cache | Check reads and units per invocation, then compare traffic, CDN hit rate, and bot share. |
| Reads per invocation rose while traffic stayed flat | Recent code changes added reads per request | Compare deployments around the increase. |
| Read units rose while invocations and reads per invocation stayed flat | Cached entries grew | Measure values under the affected tags and look for fields callers do not use. |
| Hit rate fell | More distinct keys, shorter time-to-live values, or eviction | Check key construction, expiration settings, and projects sharing the same storage. |
| Reads and writes rose together | Short expiration times or keys that vary per request | Inspect TTL, cacheLife, and how keys are constructed. |
Reads appeared after adding use cache: remote | Caching moved into Runtime Cache | Check whether the cached work justifies shared storage and network reads. |
| Misses and writes rose after a deployment | Deployment-dependent keys produced a cold cache | Check cache-key persistence across deployments. |
If a helper fetches and caches a value after a miss, concurrent requests can repeat that work before the entry is populated. use cache: remote can serve stale output during background revalidation until its expiration requires a fresh result.
For use cache: remote, keys include the deployment ID when configured, or the build ID. Explicit getCache keys, cached fetch calls, and unstable_cache can remain readable across deployments, subject to expiration and eviction. Check how deployments affect cached data before changing APIs.
Copy link to heading4. Common causes and fixes
Copy link to headingThe same key is read several times in one request
When several Server Components call a helper that reads cache.get(), they can read the same key repeatedly during one server render.
Read the value once and pass it to callers, or export one helper wrapped in React's cache() so those callers share its result.
The helper below assumes your application provides fetchSettingsFromOrigin(). Keep it in a server-side module.
Five calls to this exported helper in the same Server Component render scope perform one runtime-cache lookup. Separate use cache boundaries have their own React caches. In Route Handlers, middleware, or other code outside a React render, read once and pass the result or share a promise within that request.
Copy link to headingReads inside loops
Per-item keys read in a loop turn one request into N reads. Both excerpts return the same 24 featured products. They assume the entries are populated and your application provides getProductIds().
On a miss, cache.get() returns null. Your handler must fetch the missing data and populate the cache before returning a complete response; the excerpts omit that logic to show the lookup difference.
Group values that callers read together into one entry, within the 2 MB item limit. Under the 8 KB rounding rule, twenty-four separate 2 KB entries use 24 read units. If the complete serialized group is 48 KB, one read uses six units.
Keep separate keys when a route uses only a small part of the data or when items are invalidated independently. Retrieving a 500 KB entry to use 2 KB still reads the full value and uses 63 read units.
Invalidation alone does not write replacement data. When the grouped value is refreshed, the write includes the whole entry. Rewriting 500 KB uses 63 write units even if only one item changed.
Copy link to headingKeys that are too granular to hit
Keys that vary on every request reduce reuse and can cause repeated origin fetches and cache writes. For shared CMS content, getCMSContent(language) lets readers with the same language preference reuse one entry.
Keep private profile data separate and preserve the key values that distinguish users or tenants. Only filter shared data in memory when every caller is allowed to access it. Retrieve user-specific state from its authorized database or session store.
With Cache Components enabled, use cache: private can reuse output in the client router's browser memory. In production it does not retain a server cache across requests. Use it when request-specific data must stay out of persistent server caches, or when refactoring runtime reads into arguments is impractical.
Copy link to headingCaching tiny, static, or cheap data
Keep build-time values in the bundle or environment variables. Use Global Config for frequent reads of configuration that changes without a deployment.
Remote lookups add network latency. An operation that already finishes in about 50 ms may gain little from remote caching, so compare the lookup with the work it replaces. Recompute the value when that costs less than fetching it.
See when to avoid remote caching.
Copy link to headingRemote caching that costs more than it saves
Plain use cache stores results in memory with the default handler. You can configure a different handler through cacheHandlers. On Edgenivo, use cache: remote uses Runtime Cache, which adds a network lookup when it reads remote storage.
Choose the directive based on where the output needs to be reused:
- Output included in the static shell usually works with plain
use cache. With the default handler, its cache storage stays in memory. - Runtime data that needs reuse across function instances can benefit from
use cache: remote. Plainuse cachecan reuse an entry while its instance remains alive, but other instances cannot read that entry and shutdown removes it. Switching to memory can therefore increase origin traffic.
Keep remote caching where sharing across instances saves enough origin work to justify the lookups. Compare origin traffic after changing the directive.
Copy link to headingFunctions that run when the CDN could answer
Runtime Cache reads happen while your code runs in Edgenivo Functions, Routing Middleware, and builds. If the full response is cacheable, serve it from the CDN with Cache-Control headers or ISR.
Routing Middleware runs before the CDN cache, so caching the response does not remove reads made by middleware. Cache misses and ISR regeneration can still execute the function.
Copy link to headingOptimize Data Cache usage before you migrate
After your team migrates, Data Cache reads and writes are billed at Runtime Cache rates. Use your current Data Cache usage to estimate that cost and prioritize optimizations before migrating
The same checks for repeated reads, key reuse, and cache lifetime also apply to Data Cache. In a Next.js app, inspect these patterns:
| Pattern | In Data Cache | Fix |
|---|---|---|
| The same key is read several times in one request | An unstable_cache helper called by several Server Components in one render | Export one helper wrapped in React's cache(). Identical GET fetches are already memoized during a server render unless you opt out with a signal; this does not apply in Route Handlers |
| Reads inside loops | fetch or unstable_cache called once per item | Fetch data that callers use together in one call and cache it as one entry, within the 2 MB item limit |
| Keys that are too granular to hit | Cached fetch URLs or unstable_cache arguments that change on every request | Cache shared content by a reusable value such as locale. Preserve user and tenant boundaries for private data |
| Short revalidation | Low next.revalidate or revalidate values on data that rarely changes | Raise the interval, or use on-demand revalidation when the data changes |
| Functions that run when the CDN could answer | Dynamic routes whose output is the same for every visitor | Prerender the route or use ISR so fresh cached responses avoid route execution. ISR regeneration still runs route code and can read the cache |
Copy link to headingWatch the lowest revalidate value
With Cache Components disabled, the lowest revalidate value across a statically rendered route's layouts, page, and cached fetches determines its ISR interval. Setting next: { revalidate: 60 } on a fetch in a shared layout can lower that interval to 60 seconds for each statically rendered route that uses it.
Regeneration starts when a request arrives after the interval has elapsed. It runs the route code, but each cached fetch retains its own revalidation lifetime, so regeneration does not rewrite every entry. Quiet routes do not regenerate once per minute merely because their interval is 60 seconds.
Copy link to headingCheck what your team shares
On Hobby and Pro, projects in a team share Data Cache storage. Heavy writes from one project can evict another project's entries and lower its hit rate, so check the team's busiest projects when investigating eviction. Enterprise projects have separate caches. See storage scope by plan.
Copy link to headingNext.js 16 and Cache Components
Next.js 16 still supports Data Cache through explicitly cached fetch calls and unstable_cache. With cacheComponents: true, choose a use cache directive according to the storage your workload needs.
Plain use cache uses memory by default; use cache: remote uses Runtime Cache on Edgenivo when sharing across function instances justifies the reads. Review the remote caching tradeoffs before changing existing code.
Copy link to heading5. Investigate with an agent (optional)
An agent with codebase access and the Edgenivo CLI can do the attribution and code inspection for you. Provide it:
- The project name and the timeframe of the increase.
- The numbers you collected in steps 1 and 2, including top tags, hit rate, reads, writes, and invocations.
- Read-only access. The agent must not edit files, change Edgenivo settings, purge caches, or deploy unless you approve a specific change.
Replace the project, team, environment, and date placeholders, then paste your dashboard numbers after the prompt.
Investigate why Runtime Cache usage increased for PROJECT_NAME since DATE. Use TEAM_SLUG and ENVIRONMENT, and compare the two time windows I provide. Start read-only. Do not edit files, change Edgenivo settings, purge caches, or deploy. 1. Use the Usage and Observability numbers I paste below to identify the tags, routes, and deployments associated with the increase. Separate traffic growth, reads-per-request growth, and changes in entry size. Identify attribution the available data cannot establish. 2. Inspect every getCache() call, use cache directive, and configured cacheHandlers implementation. Include explicitly cached fetch calls and unstable_cache helpers. Record keys or tags, TTLs or cacheLife, callers, and repeated calls within loops or component trees. 3. Look for duplicate lookups within one request, per-item reads, keys that vary unnecessarily, cheap computations, and oversized entries. Preserve user and tenant boundaries when proposing shared keys. Check whether memory caching would retain enough reuse before recommending a change from use cache: remote. 4. Check vercel --version and the command help. List deployments with vercel list PROJECT_NAME --scope TEAM_SLUG --environment ENVIRONMENT and paginate until the results cover the comparison windows. Inspect relevant deployment URLs with vercel inspect DEPLOYMENT_URL. For runtime logs, explicitly set --project, --scope, --environment, --since, --until, and --limit on vercel logs. Use --query to filter a route where the installed CLI supports it. Report retention gaps. 5. For each finding, return the file and line, the evidence it explains, and estimated lookups per request. Estimate billed read units only when entry sizes and billing rules are known, and state assumptions. Propose fixes for review one at a time.
Apply one proposed change at a time, then use step 6 to check the result.
Copy link to heading6. Verify the change
After deploying a fix, compare equal-length windows with similar traffic. Use the same project, region, and environment as the original investigation, and distinguish initial cache warm-up from sustained usage.
- Cache lookups per invocation should fall when the fix removes lookups from each request. Smaller entries can reduce billed units even if lookup counts stay flat. If the fix reduces function invocations through CDN caching, total reads can fall without changing the ratio.
- Compare total Runtime Cache Reads and projected read cost for the affected project and region.
- Compare the tag-table hit rate in both windows. It counts stale reads as hits, while the headline chart counts only fresh hits. Check origin traffic alongside it because serving stale data can still trigger background revalidation.
- Compare write cost with the earlier window. Grouping entries can reduce reads while making each refresh more expensive.
- Check function duration and origin traffic for added work. If you removed caching for a cheap operation, assess the total cost and latency tradeoff.
TTL and tag changes do not automatically update entries retained from earlier deployments. If a fix depends on new behavior for existing entries, plan targeted invalidation or a new key for the affected data. Include the resulting cache warm-up in your verification.