What a Fabric Capacity Actually Is#

Every workload in Microsoft Fabric — a notebook, a warehouse query, a report refresh, a real-time stream — draws compute from the same kind of resource: a capacity. A capacity is a dedicated pool of resources that performs work in Fabric, and capacity units (CUs) are the single unit of compute measurement across the platform. Every operation draws CUs from the available pool, and each operation is classified as either interactive (user-facing, latency-sensitive) or background (batch, longer-running) [S1].

This matters more than it might sound, because a capacity is also a tenancy and isolation boundary. Each Fabric capacity is a distinct resource pool living inside a Microsoft Entra tenant. The SKU size determines how much compute the pool provides, and a tenant can hold as many capacities as it needs — often split by geography or business unit [S2]. Fabric enforces throttling at the capacity level, not the tenant level, so a saturated capacity only affects the workspaces assigned to it; every other capacity in the tenant keeps running normally [S3].

A capacity is a dedicated pool of resources that performs work in Fabric.

That isolation property is why capacity sizing is not just a cost decision — it's an architectural one. Put a noisy, bursty Spark workload on the same capacity as an executive dashboard, and the dashboard inherits the Spark job's throttling risk. Split them, and it doesn't. This article covers what a capacity buys you, how bursting/smoothing/throttling actually work under the hood, and the operational practices that keep a capacity healthy day to day.

The SKU Ladder and What CUs Buy You#

Fabric F SKUs run from F2 up to F2048, and the SKU number is its capacity units — an F64 supplies 64 CUs — with each step up the ladder doubling the compute of the step before it [S2]. There's no F96 between F64 and F128; sizing is a binary choice up the doubling curve.

The Fabric trial grants a capacity equivalent to F64 (64 CUs) for 60 days, and it behaves like F64 for Power BI viewing rights — making it a realistic sandbox for evaluating the whole platform before buying [S2].

The F64 Licensing Threshold#

F64 is more than a compute tier; it's a licensing cutover point. A capacity is a dedicated set of compute resources backing Power BI content, and Power BI Premium (P SKUs) as well as Fabric capacities of F64 or larger let users on a free license consume shared content. A capacity is also required to use Copilot [S4]. Concretely: on F64 or larger, anyone with a workspace viewer role can view Power BI content on a free license; below F64, every viewer needs a Pro or Premium Per User (PPU) license [S2].

Microsoft is consolidating purchasing around F SKUs and retiring the older Power BI Premium per-capacity P SKUs. The documentation maps F64 to the former P1 (8 v-cores) for rough compute comparison only — not licensing equivalence [S2].

Warning

Hedged, tier-6 estimate: a practitioner write-up puts non-EA Power BI Premium (P-SKU) end-of-life at January 1, 2026 for non-EA customers, and EA-term-end-or-January 1, 2028 (whichever comes first) for EA customers, positioning F64 as the functional replacement for P1. The same source describes a three-phase wind-down after the deadline passes — roughly 30 days of continued free-equivalent access, then about 90 days of throttled access, then data suspension. Neither the exact dates nor the phase lengths are confirmed by a tier-1 Microsoft source in the knowledge base yet; treat them as directionally useful planning inputs, not committed dates, and verify against current Microsoft Learn guidance before building a migration timeline around them [S9].

Shared Capacity#

Every Fabric-enabled tenant includes a shared capacity that hosts all personal My Workspaces plus Pro and PPU workspace types. New workspaces land here by default and can be reassigned to any capacity in the tenant later [S2]. Shared capacity doesn't carry the burst headroom or Azure-integration surface of a dedicated F SKU — workspaces on F SKUs specifically gain ARM APIs, Terraform support, and managed private endpoints [S2].

Note

Power BI Embedded A/EM SKUs support only Power BI items, not Fabric items. If your embedding scenario also needs a lakehouse, warehouse, or notebook, you need an F capacity alongside it [S2].

From CUs to Workload Resources#

CUs are an abstraction until you map them to what a specific engine actually gets. Several mappings are verified in the knowledge base.

Spark vCores. Each Fabric CU grants two Spark vCores, and a 3x burst multiplier applies on top. An F64 therefore exposes up to 384 Spark vCores (64 CU × 2 × 3), which is the ceiling for how large a custom Spark pool can be configured [S5]. Inference: sustained (non-burst) Spark compute on an F64 is 128 vCores — meaningfully smaller than the 384-vCore burst ceiling — so pool-sizing exercises that plan against the burst number risk overestimating steady-state throughput.

Direct Lake memory. The capacity SKU also sets the maximum memory available to each Direct Lake semantic model, bounding how much column data can stay resident at once [S6]. Larger SKUs mean more of a Direct Lake model's columns can stay warm, which is why Direct Lake sizing is partly a memory decision, not just a compute one.

Warehouse smoothing. Warehouse workloads benefit from burstable capacity — letting jobs temporarily consume extra resources for speed — and from smoothing, which spreads the evaluation of compute consumption over time so a spiky peak-hour load is offset against idle periods [S7].

Shared pool, shared consequences. The same capacity pool is genuinely shared across workload types, not just conceptually. Lakehouse and Warehouse items in the same capacity both store data as Delta Parquet in OneLake and draw from the identical F-SKU capacity unit pool — there's no separate storage choice or separate pricing tier between the two engines [S10]. Similarly, Microsoft Fabric Planning consumes CUs from the same capacity pool as every other workload: capacity that Planning leaves unused is available to other Fabric workloads on the same capacity, and vice versa [S11]. All Planning usage — interactive sessions and automation jobs alike — meters against the workspace's existing capacity units, with no separate subscription or license required [S11], and that consumption is visible in the Fabric Capacity Metrics app by filtering to Experience = Planning, which surfaces session activity, automation job counts, and CUs consumed per job [S11].

Bursting and Smoothing: How Fabric Absorbs Spikes#

Fabric uses two complementary mechanisms to keep temporary spikes from being punished outright: bursting, which lets an operation consume more CUs than the SKU's provisioned rate so it finishes quickly, and smoothing, which spreads that consumed CU cost across future timepoints instead of charging it all at once [S3].

The unit that makes this work is the timepoint — 30 seconds long, with 2,880 of them in a 24-hour window. Smoothing distributes an operation's CU cost across those future timepoints rather than billing the full amount at submission [S3].

The smoothing window is not uniform:

Operation typeSmoothing window
Interactive5 to 64 minutes, scaled by total CU consumption
BackgroundAlways the full 24-hour window

Background operations get the longer window because they typically carry much larger CU loads [S3] — a nightly warehouse batch that would spike an interactive smoothing window is instead spread thin across 2,880 timepoints.

Warning

Bursting and smoothing do not apply when a capacity admin has enabled Autoscale Billing for Spark. In that mode, Spark usage runs pay-as-you-go only, and the standard CU pool accounting stops governing it [S3]. Teams that enable Autoscale expecting the usual smoothing protection will be surprised by the billing behavior change.

One distinction is easy to lose when reading a capacity metrics chart: smoothing only changes how CU consumption is accounted for over the smoothing window — it does not change how long the underlying operation actually takes to run. A heavy query still finishes at the same wall-clock speed; smoothing only reshapes when its cost lands on the capacity's ledger (hedged — tier-6 characterization of Microsoft's smoothing behavior, not independently sourced from a tier-1 doc) [S9].

How It Works / Best Practices#

Do this: size capacity boundaries around isolation, not just cost#

Because throttling is enforced per capacity rather than tenant-wide [S3], a capacity boundary is a real isolation control, not just a billing line. Put SLA-bound interactive workloads on their own capacity, away from bursty batch or experimental work. A practitioner write-up on capacity optimization recommends a similar shape: a larger production capacity for mission-critical workloads, a small dev/test capacity for authoring, and optionally a separate capacity reserved for high-priority executive reporting that needs isolation from everything else's contention (hedged — tier-6 pattern, consistent with the tier-1 per-capacity throttling boundary above but not itself a Microsoft-documented recommendation) [S9].

text
# Wrong: one F64 capacity for everything
Capacity: fabric-prod-f64
  - Executive Power BI dashboards (interactive)
  - Nightly ELT warehouse loads (background)
  - Ad-hoc Spark exploration notebooks (interactive/background, unpredictable)

# Right: isolate the SLA-bound workload
Capacity: fabric-prod-reporting-f64      # dashboards only
Capacity: fabric-prod-batch-f32          # warehouse + scheduled Spark
Capacity: fabric-dev-sandbox-f4          # ad-hoc/exploration

With the split, a runaway exploration notebook on the sandbox capacity can throttle itself into rejection without ever touching the CU budget the executive dashboards depend on — a direct consequence of per-capacity throttling enforcement [S3].

Do this: schedule background jobs to exploit the 24-hour smoothing window#

Background operations are always smoothed over 24 hours [S3], so a large batch job is far less likely to trigger throttling than an interactive operation of equal CU cost. Explicitly scheduling heavy ELT as background-classified, off-peak work — rather than letting it run as an ad-hoc interactive session — uses the smoothing design as intended.

text
# Wrong: run a large backfill interactively during business hours
# (classified interactive -> smoothed over 5-64 minutes -> throttling risk)
notebook.run(mode="interactive", job="backfill_5y_history")

# Right: schedule it as a background pipeline run overnight
pipeline.schedule(
    job="backfill_5y_history",
    trigger="cron: 0 1 * * *",   # 01:00, background-classified
    classification="background"
)

Do this: work the optimization checklist before you buy a bigger SKU#

A practitioner write-up on capacity optimization lays out a specific ordering worth following before reaching for a SKU increase: address the highest-CU-consuming operations first, then reschedule background jobs to off-peak windows, then move dev/test workloads onto their own capacity, then consider pausing capacities during off-hours, and only then evaluate whether the SKU itself needs to grow (hedged — tier-6 practitioner sequencing, not a Microsoft-documented procedure, but consistent with the tier-1 mechanics of smoothing and per-capacity isolation described above) [S9]. The same source's right-sizing heuristic is to size a capacity for its average sustained load, not its peak — reasoning that bursting and smoothing already exist specifically to absorb short spikes, so provisioning for the worst moment of the month wastes steady-state capacity every other hour [S9].

text
# Optimization order (cheapest-to-most-expensive lever first)
1. Profile capacity metrics -> find top CU-consuming operations
2. Reschedule background jobs (ELT, refreshes) to off-peak hours
3. Split dev/test onto a separate, smaller capacity
4. Pause non-production capacities outside business hours
5. Only now: evaluate a SKU increase

Do this: monitor early and often, then settle into a steady cadence#

Before committing to a specific capacity plan, a practitioner write-up recommends collecting at least 30 days of usage data, reviewing it daily during that initial window, then shifting to weekly review once usage stabilizes (hedged — tier-6 practice) [S9]. A separate practitioner write-up on production stability makes the same point from the reliability side: monitoring on a monthly-only cadence is too coarse to catch capacity and reliability problems early, and recommends weekly review after the first month of operation instead (hedged — tier-6 practice, but the two sources independently converge on the same weekly cadence) [S12].

text
# Wrong: review the capacity metrics app once a month
# Right, in the first 30 days of a new capacity or major workload change:
Monday   -> review Fabric Capacity Metrics app (daily during ramp-up)
...
# After 30 days of stable usage:
Monday   -> weekly review only

What goes wrong: substituting PPU for a capacity#

Relying on Power BI Premium Per User (PPU) licenses to run Fabric workloads is a documented antipattern. PPU is a per-user feature set, not a capacity — it cannot create or run non-Power-BI Fabric items such as lakehouses, warehouses, or notebooks. An F or Trial capacity is still required [S2].

text
# Wrong assumption
"Our analysts all have PPU licenses, so we don't need a Fabric capacity
 to build the lakehouse and warehouse for this project."

# Reality
PPU governs *Power BI consumption rights per user*.
Lakehouses, warehouses, and notebooks need an F-SKU or Trial *capacity*
regardless of what license the analysts hold.

Provisioning a new capacity while tenant-level private links are active can take up to 24 hours to propagate into the private DNS zone, and the tenant-level private link configuration blocks tenant migrations entirely for as long as it's enabled [S8]. Teams that provision a capacity right before a go-live and expect it reachable immediately will find it unreachable until DNS propagates.

What goes wrong: skipping the post-resume warm-up query#

After a paused capacity resumes, a practitioner write-up on warehouse optimization recommends running a warm-up query against key tables right away, since the first queries after resume can otherwise hit stale-statistics or cold-cache delays (hedged — tier-6 practice, not independently confirmed by a tier-1 source) [S13]. Pausing capacities off-hours is a legitimate cost lever (see the optimization checklist above), but skipping the warm-up step trades that saving for a slow, unpredictable first few minutes of the business day.

text
# Wrong: resume the capacity and let the first live business query pay the cold-cache cost
06:00  capacity.resume("fabric-prod-batch-f32")
06:01  first_dashboard_query()   # hits stale stats / cold cache

# Right: warm it up first
06:00  capacity.resume("fabric-prod-batch-f32")
06:01  run_warmup_query("SELECT TOP 1 * FROM key_fact_table")
06:03  first_dashboard_query()   # cache and stats already primed
Original diagram: Fabric capacity smoothing and throttling thresholds

The Four-Stage Throttling Model#

Throttling is staged progressively across four thresholds, keyed to how much future capacity has already been committed by smoothed operations — not to instantaneous CU usage [S3]:

Future usage consumedInteractive operationsBackground operations
Up to 10 minutesNo throttling (overage protection)No throttling
10–60 minutes20-second delay on new submissionsNo throttling
60 minutes–24 hoursRejected outrightMay still start
Beyond 24 hoursRejectedRejected

A few clarifications the table doesn't capture on its own. Operations already in flight when a capacity enters a throttling state are always allowed to run to completion — only new submissions are delayed or rejected [S3]. When a request is rejected, the caller receives the CapacityLimitExceeded status along with a message indicating compute capacity limits have been reached [S3]. A practitioner write-up on warehouse optimization describes the same rejection stage surfacing to end users as error code 24801 (hedged — tier-6 detail, not independently confirmed against Microsoft's own error-code documentation in the knowledge base) [S13].

Three workloads deliberately deviate from this ladder. Fabric Eventstreams reduce allocated CU resources rather than delaying or rejecting new stream operations. Real-Time Intelligence skips the 20-second interactive delay stage entirely and only starts throttling at the 60-minute rejection threshold. Warehouse operations are classified as background to get 24-hour smoothing, though a background operation triggered by an interactive chain can be promoted to interactive throttling rules [S3].

Fabric also protects against multiplicative penalties: a single end-user request that fans out into a chain of dependent operations — a report visual querying a semantic model that reads from OneLake, for example — is throttled at most once per capacity in that chain [S3].

Internals#

Architecture & design#

A Fabric tenant can host multiple capacities, each an independently throttled resource pool tied to an Entra tenant and sized by its SKU [S2]. Workspaces are assigned to exactly one capacity at a time (defaulting to the tenant's shared capacity [S2]), which is what makes capacity the enforcement boundary for throttling rather than the tenant as a whole [S3]. Above the capacity layer, licensing and workload-specific rules attach: the F64 threshold gates free-license Power BI viewing [S2], and per-workload CU conversions (two Spark vCores per CU, times a 3x burst multiplier [S5]; a Direct Lake memory ceiling [S6]) determine what a given SKU actually delivers to each engine running on it. That shared-pool design extends across engine boundaries too — Lakehouse and Warehouse items in the same capacity draw from the identical CU pool and the same OneLake storage, with no separate pricing tier between them [S10], and newer workloads like Fabric Planning are metered the same way, against the workspace's existing capacity rather than a separate allocation [S11].

How it works internally#

At the center of a capacity's runtime behavior is the timepoint — a fixed 30-second accounting slice, 2,880 of which make up a rolling 24-hour ledger [S3]. Every operation submitted to the capacity is classified interactive or background at submission time, before it actually executes; if the classifier can't yet tell which it is, it defaults to background, since that's the more lenient smoothing window and therefore the user-favoring choice. The classification can be revised once runtime information becomes available [S3].

Once classified, an operation's CU cost is smoothed — spread across future timepoints rather than billed all at once — over 5 to 64 minutes for interactive work, or the full 2,880-timepoint (24-hour) window for background work [S3]. Committing an operation's smoothed cost adds to a running total of future capacity usage. As long as that total stays under 10 minutes of the SKU's per-timepoint provision, nothing happens — this is the overage-protection zone [S3]. Beyond that, the excess is recorded as a carryforward balance, and each subsequent timepoint that has idle capacity chips away at that balance through burndown; the capacity remains in a throttled state until carryforward reaches zero [S3]. In effect, throttling state is a single running variable — accumulated future obligations as a fraction of the SKU's provision — that transitions between the four throttling stages at each timepoint boundary as new commitments are added or idle capacity burns them down [S3].

Compound throttling protection is implemented by tracking a request's lineage across the workloads it fans out through, so a report-to-semantic-model-to-OneLake chain is charged (and throttled, if applicable) once per capacity rather than once per hop [S3].

Original diagram: Fabric capacity sizing decision tree - when to scale up the SKU, enable Autoscale Billing for Spark, or split workloads across capacities, with a sizing checklist covering Spark vCore ceilings, Direct Lake memory bounds, and the F64 licensing threshold

Performance characteristics#

Coming soon — this depth isn't in the knowledge base yet. It needs an L4/L5 source such as a Microsoft engineering blog or internals deep-dive publishing measured throttling/smoothing benchmark numbers (e.g. observed burndown rates under sustained load, or empirical CU-to-latency curves by SKU). Tracked in content/queue.md.

Worked Example: Sizing an F64 for a Mixed Workload#

Consider a team planning a single F64 capacity to run three workloads: interactive Power BI reports on Direct Lake models during business hours, a nightly warehouse ELT batch, and ad-hoc Spark notebooks for transformation work.

Spark vCore budget. F64 gives 64 CU × 2 × 3 = 384 burst Spark vCores, 128 sustained [S5]. If the notebooks run concurrently with business-hours reporting, they compete for the same CU pool as the interactive dashboards.

Licensing. At F64, any workspace viewer with a free license can see the reports [S2] — the team doesn't need to buy Pro licenses for every consumer.

Throttling exposure. The nightly warehouse batch is background-classified and smoothed over the full 24-hour window [S7] [S3]. Inference: if it runs 00:00–02:00, its smoothed cost is still being charged into the following morning's timepoints; if the batch is large relative to F64's provision, it can push the capacity into the overage or rejection zone right as business-hours interactive traffic starts.

Direct Lake memory. F64 sets the memory ceiling for how much of the Direct Lake model's columns can stay resident [S6]. Inference: a model that regularly exceeds that ceiling will fall back to cold reads more often, adding latency exactly when interactive traffic peaks.

Mitigation, following the optimization checklist. Before assuming the answer is a bigger SKU, apply the practitioner ordering from the best-practices section: profile which operation is the actual CU hog, reschedule the warehouse batch to finish well before business hours so burndown clears most of its carryforward balance before reports are queried, and move the Spark notebooks to a separate capacity (or Autoscale Billing) so their unpredictable load can't threaten the reporting capacity's throttling budget [S9] — the same isolation principle from the best-practices section above, applied concretely. Add a short warm-up query after any scheduled pause/resume cycle on the batch capacity so the first morning query isn't paying a cold-cache tax on top of everything else [S13].

text
Capacity: fabric-prod-reporting-f64
  06:00-22:00  Direct Lake interactive reports (protected)
  00:00-02:00  Warehouse ELT batch (background, smoothed 24h)
  --           Spark notebooks moved OFF this capacity entirely

Capacity: fabric-prod-batch-f32   # if paused off-hours
  02:05  run_warmup_query("SELECT TOP 1 * FROM key_fact_table")