Firebase Quota Exceeded: Find the Runaway and Fix It

Firestore reads, Functions invocations, Hosting bandwidth — each free-tier metric has a hard line. Here's how to find which one tripped and stop the bleeding.

Your Firebase project is fully broken: Firestore reads fail with 8 RESOURCE_EXHAUSTED: Quota exceeded, Functions return 503, Hosting serves quota errors — and you didn’t touch any code. The console banner reads Quota exceeded for quota metric .... This is almost always one of two things: a Spark (free) plan metric that hit its daily hard cap, or a runaway loop chewing through your allowance.

Fastest fix: open Firebase ConsoleUsage and billing and find the one metric sitting at 100%. If it’s Firestore reads/writes, the daily quota resets around midnight US Pacific time — so service self-heals overnight unless the runaway is still running. If you can’t wait, upgrade to Blaze (pay-as-you-go) and set a budget alert in the same session (Step 2 below). But do not stop there: find what burned the quota, or it refills and overflows again tomorrow.

Mental model: Firebase isn’t one quota. It’s dozens of independently metered metrics — Firestore reads / writes / deletes / stored GB, Functions invocations / GB-seconds, Hosting data transfer, Cloud Storage operations, phone-auth verifications. Any single one hitting its cap takes down whatever depends on it.

What changed in 2026 (read this first)

Two plan changes break older Firebase tutorials. Verify against your project before you debug:

  • Cloud Functions now require the Blaze plan. You cannot deploy or run Cloud Functions on Spark anymore. If you have working Functions, you are already on Blaze — which means you do not have a hard cap; you have a runaway burning real money. (The old “125K invocations/month free on Spark” number is gone; the Blaze free allowance is 2M invocations/month, then billed.)
  • Cloud Storage was removed from Spark, effective Feb 3, 2026. Spark projects lose access to default buckets and Storage API calls return 402/403. If your storage broke, it’s not a quota — it’s the plan change. You need Blaze to use Cloud Storage at all.

So before anything: confirm whether you’re on Spark (hard daily caps, can’t overspend) or Blaze (free allowance then billed, can overspend). Console → top-left gear / Usage and billingDetails & settings shows your plan.

Free-tier limits (as of June 2026)

These are the lines you can hit. Spark caps are hard (service shuts off, can’t overspend); Blaze gives the same allowance free, then bills you.

MetricFree allowancePlan
Firestore reads50K / daySpark + Blaze
Firestore writes20K / daySpark + Blaze
Firestore deletes20K / daySpark + Blaze
Firestore stored data1 GiB totalSpark + Blaze
Hosting stored data10 GBSpark + Blaze
Hosting data transfer360 MB / daySpark + Blaze
Functions invocations2M / monthBlaze only
Functions GB-seconds400K / monthBlaze only
Cloud Storage stored5 GBBlaze only
Cloud Storage downloads1 GB / dayBlaze only
Phone auth verificationsvaries by region, small daily capSpark + Blaze

Note: the old Hosting “10 GB/month bandwidth” line is no longer how it’s billed — Hosting free transfer is now 360 MB/day (roughly 10.5 GB across a 30-day month, but it’s a daily gate, so one viral day trips it even if your month is light).

Which bucket are you in?

SymptomMost likely causeJump to
Reads at 100%, user count normalClient listener loop or missing limit()Causes 3, 7 / Steps 5, 7
Reads spiked 100x, users flatOpen security rules + scraper/attackerCause 6 / Step 3
Functions invocations in the millionsFunction self-trigger loopCause 2 / Step 4
Hosting transfer huge vs. trafficLarge file / video served from HostingCause 4 / Prevention
Storage 402/403, was working beforeCloud Storage left Spark (Feb 2026)Upgrade to Blaze
Stored data over 1 GiB, few usersNo TTL, logs/sessions piling upCause 5

Common causes

Ordered by hit rate, highest first.

1. Spark plan hit a daily hard cap

Spark is a hard ceiling — you cannot overspend, the metric just stops at the line. Firestore reads (50K/day) is the most common one. Service comes back at the next daily reset (midnight Pacific) if the thing draining it has stopped.

How to spot it: Console → Usage and billing → the metric at 100%. Error in client logs: 8 RESOURCE_EXHAUSTED: Quota exceeded.

2. Function loop burning invocations

Classic anti-pattern — a Firestore write trigger that writes back to the same collection:

// Write to Firestore triggers onWrite → which writes Firestore → ...
exports.onUserUpdate = onDocumentWritten('users/{uid}', async (event) => {
  await db.doc(`users/${event.params.uid}`).update({...}); // triggers itself
});

On Blaze (where Functions live now) this can burn through the 2M free invocations in hours and then bill you for the rest of the month.

How to spot it: Console → Functions → a single function with absurd invocation counts (millions). Also visible in Cloud Logging.

3. Client loop reading Firestore

function Comp() {
  useEffect(() => {
    onSnapshot(query, snap => {
      setItems(snap.docs);  // re-render → useEffect runs again → subscribe again
    });
  }); // missing deps array
}

A handful of active users can drain 50K reads in under an hour.

How to spot it: Console → FirestoreUsage → reads/sec abnormally high relative to active users.

4. Large files / video blow Hosting data transfer

A 100 MB video on Firebase Hosting served to a few hundred viewers blows past the 360 MB/day free transfer in minutes.

How to spot it: Hosting data transfer huge relative to your actual traffic.

5. No Firestore TTL — old data piles up

Logs / sessions / analytics written to Firestore without cleanup eventually fill the 1 GiB stored-data cap.

How to spot it: Stored data over 1 GiB while real user-data volume is small.

6. Scraper / attacker hitting your data

Loose Firestore security rules let anyone with your client config query any collection directly. A scraper finds the endpoint and reads/writes explode.

How to spot it: User count flat, but reads spike 100x. Check Firestore Usage graph for a step change with no product launch behind it.

Shortest path to fix

Step 1: Identify which quota tripped

Firebase Console → Usage and billing
  → review Firestore / Functions / Hosting / Storage / Auth metrics
  → the one at 100% (or, on Blaze, the one whose graph hockey-sticked) is your culprit

Read the dashboard. Don’t guess.

Step 2: Emergency relief — Blaze + a budget alert

If you need service back now (Spark won’t refill until midnight Pacific), upgrade to Blaze and set a budget alert in the same session:

1. Google Cloud Console → Billing → Budgets & alerts → Create budget
2. Budget amount: the max monthly spend you'll tolerate (e.g. $20)
3. Alert thresholds: 50%, 90%, 100%
4. Alert recipients: your email
5. Optional hard stop: wire the Pub/Sub budget notification to a Cloud Function
   that calls projects.updateBillingInfo to disable billing

Reality check: a budget alert does not cap spend — it only emails you. The only true hard stop is the Pub/Sub-triggered “disable billing” automation, and even that bills for charges already incurred before it fires. (Google’s own capping guide walks through the Cloud Function.) For a hard ceiling with zero overspend risk, stay on Spark and fix the runaway instead.

Step 3: Locate and stop the runaway

Source = client loop / Function loop / attacker

Function loop:
  Console → Functions → find the one with insane invocations → Disable it
  (or delete the deployment, then redeploy a fixed version)

Client loop:
  Roll back the latest frontend deploy
  Or hotfix the useEffect (add a deps array — Step 5)

Attacker / open rules:
  Lock everything down immediately, then re-open only what you need:
// Emergency lockdown — paste, deploy, breathe, then re-open selectively
match /{document=**} { allow read, write: if false; }

Deploy with firebase deploy --only firestore:rules.

Step 4: Guard Functions against self-triggering

exports.onUserUpdate = onDocumentWritten('users/{uid}', async (event) => {
  // If our own _serverUpdate flag is already set, this write came from us — bail.
  const after = event.data?.after.data();
  if (after?._serverUpdate) return;

  await db.doc(`users/${event.params.uid}`).update({
    derived: ...,
    _serverUpdate: true,  // mark so the next trigger short-circuits
  });
});

Cleaner still: split source and derived data into two collections so the write path can’t feed back into the trigger.

Step 5: Fix the client useEffect deps

useEffect(() => {
  const unsub = onSnapshot(query, snap => setItems(snap.docs));
  return () => unsub();   // clean up the listener on unmount
}, []);   // empty deps: subscribe once, not on every render

Step 6: Tighten Firestore rules (least privilege)

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{uid} {
      allow read, write: if request.auth.uid == uid;
    }
    match /posts/{post} {
      allow read: if true;
      allow write: if request.auth != null
                   && request.resource.data.authorId == request.auth.uid;
    }
  }
}

Deploy: firebase deploy --only firestore:rules. Test rules first in Console → FirestoreRulesRules Playground before deploying.

Step 7: Reduce unnecessary reads

// Slow: full fetch every time
const all = await getDocs(collection(db, 'posts'));

// Fast: paginate + cache
const first = await getDocs(query(
  collection(db, 'posts'),
  orderBy('createdAt', 'desc'),
  limit(20)
));

// In React, use SWR or TanStack Query to cache and dedupe fetches

How to confirm it’s fixed

  1. Console → Usage and billing: the metric that was at 100% should stop climbing (or, on Blaze, the reads/sec graph should drop back to baseline within a few minutes).
  2. Functions tab: invocation rate back to a sane number, no single function spiking.
  3. Client: no more 8 RESOURCE_EXHAUSTED in your logs; reads succeed.
  4. On Spark, full service returns at the next midnight-Pacific reset — but only if the runaway is dead. If the metric refills and trips again the next day, you stopped the symptom, not the source: go back to Step 3.

Prevention

  • Set Google Cloud budget alerts (50% / 90% / 100%) before launching, and wire the Pub/Sub “disable billing” automation if you’re on Blaze.
  • Default-deny Firestore rules; open only the exact paths you need. Never ship allow read, write: if true;.
  • A Function must never subscribe to a collection it also writes to. If it must, use a flag to break the loop (Step 4).
  • Always give useEffect a deps array; enforce it with eslint-plugin-react-hooks.
  • Always add limit() to Firestore queries, even in development — an unbounded getDocs over a growing collection is a slow-motion quota bomb.
  • Don’t serve large files or video from Firebase Hosting (360 MB/day free transfer). Use R2 / S3 + a CDN.
  • Add Firestore TTL policies for logs / sessions / analytics so stored data self-cleans.
  • Monitor reads/sec and writes/sec; alert on anomalous spikes.
  • Estimate monthly cost before launch — e.g. (daily reads × 30 ÷ 100000) × $0.06 + ... for Firestore reads — so you know what “normal” looks like.

FAQ

Why did Firebase break when I changed nothing? A free-tier metric hit its daily cap — usually Firestore reads (50K/day) — driven by a client listener loop, a Function loop, or a scraper exploiting open rules. The trigger built up over hours, then crossed the line all at once.

Will it fix itself? On Spark, daily Firestore quotas reset around midnight US Pacific time, so service returns overnight — but only if whatever drained the quota has stopped. If it’s still running, it just trips again the next day.

Spark or Blaze — which protects me from a surprise bill? Spark. It can’t overspend; metrics simply stop at the cap. Blaze gives the same free allowance but then bills with no hard cap unless you build the Pub/Sub “disable billing” automation yourself. A budget alert alone only emails you.

My Cloud Storage / Cloud Functions suddenly returns 402/403 or won’t deploy. Quota? No — both now require the Blaze plan. Cloud Storage left Spark on Feb 3, 2026, and Functions have required Blaze for a while. Upgrade the project to Blaze (and set a budget alert) to use them.

How do I find the exact function that’s looping? Console → Functions shows per-function invocation counts; sort by it. Cloud Logging filtered to that function’s name shows whether each run is re-triggering a write. Disable the function to stop the bleed before you fix the code.

Does browsing data in the Firebase Console count against my read quota? Yes — the Console reads Firestore to render documents, and that counts. Avoid hammering large collections in the Data tab while you’re already near the cap.

Tags: #Firebase #Debug #Troubleshooting