open atlas
↑ Back to track
Next.js, zero to senior NEXT · 10 · 03

Cost engineering: the five line items and the patterns that 10x them

Decompose the bill into GB-hours, image transformations, egress, ISR ops and edge invocations — then hunt the multipliers: the resize-farm optimizer, middleware on every asset, revalidate:1 on a long tail, the accidental-dynamic tax. Per-route math plus guardrails.

NEXT Senior ◷ 18 min
Level
FoundationsJuniorMiddleSenior

The CFO forwards the invoice with one word: “explain.” The bill went from $1,900 to $6,400 in a month; traffic grew 8%. The decomposition takes twenty minutes and answers everything: function GB-hours flat, egress flat, image optimization up 14x — 1.3 million transformations against last month’s 90 thousand. The access logs show why: a partner blog hotlinked product images through /_next/image with its own w=3840&q=100 parameters, and remotePatterns still contained the wildcard somebody added during prototyping. The optimizer had become a free public resize farm for other people’s websites. The fix was a six-line config diff. The expensive part came next: auditing per-route cost for the first time, the team found a cookies() call in the root layout that had been making the entire marketing site render dynamically — every page, every request, since March. Nobody had noticed, because nothing was broken. The bill was the only monitor that saw it.

Reading the bill: five line items

When your CFO forwards an invoice, can you answer “which meter caused this” in twenty minutes? If not, this section gives you the framework to do it every time. A serverless Next.js bill decomposes into five meters, and each one is driven by a different part of your architecture. Function GB-hours — execution time times allocated memory, summed over invocations — is driven by how much of your traffic renders dynamically and how heavy each render is. Image optimization bills per transformation: each unique combination of source URL, width, and quality is one resize; repeat requests are cache hits and cost roughly nothing, so the meter counts unique variants, not image views. Bandwidth/egress is dollars per GB out: client bundles, images, RSC payloads, API responses. ISR reads and writes meter operations against the cache store that backs the Full Route and Data Caches. Edge/middleware invocations bill per million requests that pass through middleware or edge routes.

Which line dominates depends on the app’s shape, and guessing wrong wastes an optimization quarter. A content site with good caching spends on images and egress, and its GB-hours round to zero. A logged-in SaaS dashboard is the inverse: everything renders dynamically per user, so GB-hours dominate and images are noise. A big ISR catalog pays a third profile: reads and writes on the cache store plus regeneration compute. The first discipline of cost engineering is the twenty-minute decomposition from the Hook — name the dominant line item before optimizing anything, because the team’s intuition is usually a release behind the truth.

The patterns that 10x a bill

Each of these is a mechanism, not a vibe — and each has a one-screen fix.

The optimizer as a public resize farm. /_next/image is an open HTTP endpoint. With a remotePatterns wildcard, anyone on the internet can feed it any image URL at any width and quality, and every unique combination is a billed transformation plus a cache entry you store. Hotlinkers and scrapers enumerate widths for free. Fix: an explicit allowlist in remotePatterns, trimmed deviceSizes/imageSizes (every extra width multiplies the variant space), and a raised minimumCacheTTL so re-transforms amortize.

Middleware on every static asset. Without a matcher, middleware runs on every request — including the 40-50 /_next/static chunks, images, and favicons each pageview pulls. Your invocation count is not pageviews; it is pageviews times assets-per-page. The canonical matcher excludes _next/static, _next/image, and favicon.ico, cutting the multiplier from ~46x to 1x — and removing the added latency hop from every asset fetch.

revalidate: 1 on a long-tail catalog. A trafficked page with revalidate: 1 regenerates up to once per second: 86,400 potential writes per page per day. A hundred hot pages is 8.6 million daily ISR writes — for content that actually changes weekly. The honest window plus tag-based on-demand invalidation gives fresher content than the panic value, at four orders of magnitude fewer writes.

The accidental-dynamic tax. One cookies() call in a shared layout opts every route under it out of the Full Route Cache. A heavy RSC tree that should render once per deploy now renders per request: 5M requests a month times 0.4 GB-s each is 2M GB-s for pages whose HTML never differs. This is the pattern from the Hook, and the bill is its only symptom — latency stays acceptable, pages look right, the meter just runs 24/7. Audit: list routes by render mode and challenge every dynamic marketing page.

Bundles re-downloaded every deploy. /_next/static/* filenames are content-hashed — built to be served with Cache-Control: public, max-age=31536000, immutable. A self-host reverse proxy that drops or weakens those headers makes every user re-download every chunk after every deploy: users times megabytes times deploys-per-week of pure egress, plus the latency regression nobody attributes correctly.

Quiz

Image optimization jumped 14x in a month while traffic grew 8%. Logs show /_next/image requests for a partner site's image URLs with w=3840&q=100 and external referers. What is the billing mechanism behind the jump?

Per-route math, the crossover, and guardrails

Before you decide whether to self-host or move to a managed platform, ask: what does this specific route actually cost, and is that what you intended? Cost decisions are per route, so do the arithmetic per route. A dynamic /pricing page at 1M requests/month, 280 ms at 1.7 GB allocated, costs about 0.48 GB-s per request — 480k GB-s a month. At raw infrastructure rates that is single-digit dollars; through a managed platform’s per-request pricing the same shape lands at a multiple of that — but the comparison that matters is against the alternative: rendered once per deploy and served from the CDN, the same page costs approximately zero compute and pennies of egress. The ratio between “dynamic by accident” and “static as designed” is effectively infinite, which is why the accidental-dynamic tax deserves its own audit. Run this math in the other direction too: a route that genuinely personalizes every response is supposed to cost GB-hours — the goal is a bill that matches intent, not a minimal bill.

The self-host crossover from the deployment unit gets sharper once you decompose. Steady traffic — say a sustained 80 req/s with bounded spikes — is flat-capacity-shaped: a pair of VMs plus a commodity CDN lands near 10-15% of a managed bill at the same profile, and the image pipeline (sharp on your own CPU behind CDN caching) is usually the single biggest line you can repatriate. Spiky, low-floor traffic inverts the math: per-request pricing beats paying for idle capacity, and the operational cost of running the fleet eats the savings. The decomposition tells you which shape you are — and whether to move everything, nothing, or just the image line.

Guardrails make this a system instead of a heroic quarterly audit. Spend alerts per line item, not per total — the Hook’s 14x image jump was a 3.4x total, and a 40x jump on a small line hides inside a 1.5x total for weeks. Cost review in the PR template for new routes: four questions — static or dynamic, and is that intentional? what request APIs does it touch? does it add image sources or widths? does it change the middleware matcher? Each question maps to one of the five meters. The monthly top-10 ritual: rank routes by requests times GB-s (add transformation counts for image-heavy apps), read the list out loud in fifteen minutes, and fix the head. The list is boringly stable — which is exactly why the one month it changes is the month you catch the regression early.

Quiz

A marketing site's pages look right and load fast, but function GB-hours have run high for months. The root layout calls cookies() to read a theme preference. What is the cost mechanism, and the cheapest fix?

Recall before you leave
  1. 01
    Name the five line items of a serverless Next.js bill and which app shape maxes each one.
  2. 02
    Give three 10x patterns with their mechanisms and one-screen fixes.
Recap

Cost engineering starts with decomposition: the bill is five meters — function GB-hours driven by dynamic render share and render weight, image transformations counted per unique source-width-quality variant, egress per GB out, ISR reads and writes against the cache store, and edge invocations through middleware — and which one dominates is your app’s shape: content sites burn images and egress, SaaS dashboards burn GB-hours, ISR catalogs burn cache ops. The patterns that 10x a bill are mechanical and each has a one-screen fix: a remotePatterns wildcard turns /_next/image into a public resize farm where strangers mint billed variants (allowlist, trim the size arrays, raise minimumCacheTTL); middleware without a matcher runs on every static asset and multiplies invocations by assets-per-page (canonical exclusion matcher); revalidate:1 on a long tail writes up to 86,400 times per page per day for weekly-changing content (honest windows plus tag invalidation); one cookies() call in a layout converts an entire static site into per-request renders whose only symptom is the invoice (narrow the dynamic boundary); and a self-host proxy that weakens immutable caching on content-hashed /_next/static files re-downloads the bundle set per user per deploy. Do the math per route — requests times GB-s versus approximately-zero static serve — and revisit the self-host crossover with decomposed numbers: steady-traffic shapes repatriate well (the image pipeline first), spiky low-floor shapes do not. Then institutionalize it: spend alerts per line item because a 40x line hides inside a mild total, cost questions in the PR template mapped to the five meters, and the monthly top-10-expensive-routes ritual so the regression is caught the month it ships. Now when you see an invoice spike, you will decompose before you optimize — name the dominant meter, find the mechanism, and reach for the one-screen fix instead of guessing.

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 6 done

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

Apply this

Put this lesson to work on a real build.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.