Your Firebase project is fully broken: Firestore reads fail, Functions all 503, Hosting returns quota errors — and you didn’t touch any code. The console banner says “Quota exceeded for quota metric …” This is either Spark (free) hitting a hard cap, or Blaze hitting a per-day quota. The real problem isn’t that the quota filled up — it’s that you never set an alert, so you only found out when it overflowed.
Mental model: Firebase isn’t one quota — it’s dozens of metered metrics (Firestore reads / writes / deletes / storage GB / Functions invocations / GB-seconds / Hosting bandwidth / Auth verifications…). Any single one hitting its cap = 503.
Common causes
Ordered by hit rate, highest first.
1. Spark plan hit a hard cap
Spark is a hard ceiling (cannot overspend). Common red lines:
Firestore reads: 50K / day
Firestore writes: 20K / day
Firestore deletes: 20K / day
Firestore storage: 1 GB
Functions invocations: 125K / month
Hosting bandwidth: 10 GB / month
Auth verifications: unlimited (but phone auth = 100 / day)
How to spot it: Firebase Console → Usage and billing → which is at 100%.
2. Function loop burning invocations
Classic anti-pattern:
// Write to Firestore triggers onWrite → which writes Firestore → ...
exports.onUserUpdate = onDocumentWritten('users/{uid}', async (event) => {
await db.doc(`users/${event.params.uid}`).update({...}); // ❌ triggers self
});
Can burn through a month’s free quota in 24 hours.
How to spot it: Console → Functions → single function with absurdly high invocations (millions).
3. Client loop reading Firestore
function Comp() {
useEffect(() => {
onSnapshot(query, snap => {
setItems(snap.docs); // re-render → useEffect → subscribe again
});
}); // ❌ missing deps array
}
10 active users can drain free quota in an hour.
How to spot it: Console → Firestore → Usage → unusual reads/sec.
4. Large files / video bust Hosting bandwidth
100MB video on Firebase Hosting × 1000 viewers = 100GB, 10x the free quota.
How to spot it: Hosting bandwidth absurdly high relative to traffic.
5. No Firestore TTL, old data piles up
Logs / sessions / analytics stored in Firestore without cleanup — eventually fills 1GB.
How to spot it: Storage > 1GB but user-data volume doesn’t justify it.
6. Scraper / attacker hitting your data
Loose Firestore security rules → attacker finds the endpoint and queries anything → bandwidth and reads explode.
How to spot it: User count unchanged but reads spike 100x.
Shortest path to fix
Step 1: Identify which quota tripped
Firebase Console → Usage and billing
→ review Firestore / Functions / Hosting / Auth metrics
→ the red 100% one is your culprit
Read the dashboard — don’t guess.
Step 2: Emergency relief — Blaze + hard caps
If you need to restore service now, upgrade to Blaze (pay-as-you-go), but set Budget Cap and Alerts immediately:
1. GCP Console → Billing → Budgets & alerts → Create budget
2. Budget amount: $20 (the max monthly you'll tolerate)
3. Alert thresholds: 50%, 90%, 100%
4. Alert recipients: your email
5. Important: enable "Disable billing" action to prevent runaway
Note: Blaze can’t truly hard-cap spend — it’s alert + auto-disable. Already-incurred charges still bill.
Step 3: Locate the runaway
Source = client script / Function loop / attacker
Function loop:
Console → Functions → find the one with insane invocations → Disable
Client loop:
Roll back the latest frontend
Or hotfix the useEffect with deps array
Attacker:
Tighten Firestore rules immediately (match /{document=**} { allow read, write: if false; })
Then carefully add back the rules you actually need
Step 4: Guard Functions against self-triggering
exports.onUserUpdate = onDocumentWritten('users/{uid}', async (event) => {
// If triggered by our own _serverUpdate flag, skip to avoid loop
const after = event.data?.after.data();
if (after?._serverUpdate) return;
await db.doc(`users/${event.params.uid}`).update({
derived: ...,
_serverUpdate: true, // mark
});
});
Or cleaner — split into two collections to isolate write paths.
Step 5: Fix client useEffect deps
useEffect(() => {
const unsub = onSnapshot(query, snap => setItems(snap.docs));
return () => unsub();
}, []); // ✅ empty deps
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
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 / TanStack Query to cache and dedupe fetches
Prevention
- Set GCP Budget Alerts (50% / 90% / 100%) before launching any project — email notifications
- Tight Firestore rules — default deny, allow only what you need
- Functions never subscribe to a collection they also write to; if you must, use a flag to break the loop
- Always provide a deps array to useEffect; enforce with eslint
- Don’t host large files on Firebase Hosting — use R2 / S3 + CDN
- Add Firestore TTL policies for logs / sessions / analytics
- Monitor reads/sec and writes/sec; alert on anomalous spikes
- Estimate monthly cost before launch:
(daily reads × 30 × $0.06/100K) + ...so you know what’s normal
Related
- Firebase deploy permission denied
- Firebase Function not found
- Firestore composite index missing
- Rate limit issue
Tags: #Firebase #Debug #Troubleshooting