Turn off your Firestore automatic indexes
query(blogPosts, where("id", "==", "firestore-indexes"))
Every Firestore query that contains a where clause – such as the one above – requires an index. Some database engines would service a query without an index by scanning the entire table. That’s slow! Not Firestore. Firestore will refuse the request if the necessary index isn’t there.
To make this zero-effort for one-field queries like the one above, Firestore automatically maintains single-field indexes for each field in each document. It does so in both ascending and descending order so results can be returned in either order depending on the query’s orderBy clause.
Handy, right?
Storage Size – An Interactive Example #
Trouble is, those single-field index entries add up.
To see just how much, edit the JSON below and see the resulting storage usage for the automatic index entries. Calculations are based on Firestore – Storage size calculations and assume you are using the default indexing rules.
{
"question": {
"type": "time-matching",
"value": { "hour": 2, "minute": 25 }
},
"answer": {
"type": "digital",
"digits": ["1", "4", "2", "5"]
},
"takenAtDate": "2025-12-19T15:00:00.000Z"
}
| Name | 50 B |
| Field data | 134 B |
| Additional bytes | 32 B |
answer.digits | 556 B |
takenAtDate | 320 B |
question.value.minute | 306 B |
question.type | 302 B |
question.value.hour | 302 B |
answer.type | 286 B |
Why do index entries take so much storage? #
- Document Name700 B
- Parent Document Name574 B
- Additional Bytes448 B
- Field Name216 B
- Field Value134 B
Each individual index entry can be surprisingly large. Entry sizes are sensitive to the length of the document path since each index entry has to reference the entire document path and the parent document path. Entries also include a non-trivial fixed overhead of 32B. The field value is often an insignificant portion of the index entry size.
And as for how many entries we get for each document, remember it’s often 2 indexes (and therefore 2 index entries) per field: ascending + descending. Fields nested within map fields are indexed too. For array fields, it’s an entry per element of the array, another way you can rack up more entries than you might expect.
“Storage is cheap” #
The size of the index entries in your Firestore database is also multiplied into your various backups, which you may want to have plenty of. For us at Polymath, Firestore storage (including backups) was accounting for a considerable 34% of our total Google Cloud bill.
We were able to chop nearly 29% off our total bill solely by being more discerning with our Firestore indexes. Your mileage, of course, may vary.
How to turn them off #
Once you’ve found fields that you’re confident you won’t be querying on, there are options for how you turn off the automatic indexes. I’ll assume you’re using a firestore.indexes.json file. Read more in Cloud Firestore Index Definition Reference.
You can turn off automatic indexes for fields one by one by setting the list of indexes to an empty list:
// firestore.indexes.json
{
"fieldOverrides": [
{
// we can only specify collectionGroup, not the collection path
// so this applies to e.g. both:
// users/alice/books and libraries/cambridge/books
"collectionGroup": "books",
// If the field is a map
// than nested fields inherit these settings
"fieldPath": "blurb",
"ttl": false,
"indexes": []
}
]
}
You can turn them off for all fields within a specific collection group using the * wildcard and selectively enable the ones that you need:
// firestore.indexes.json
{
"fieldOverrides": [
{
"collectionGroup": "books",
"fieldPath": "*",
"ttl": false,
"indexes": []
},
{
"collectionGroup": "books",
"fieldPath": "isbn",
"ttl": false,
"indexes": [
// here we choose to only index in ascending order
{
"order": "ASCENDING",
"queryScope": "COLLECTION"
}
]
}
]
}
Unfortunately, you can’t turn them off database-wide, which seems to be a deliberate move on Firestore’s part. Trying to use the default collection group name __default__ fails:
// firestore.indexes.json
{
"fieldOverrides": [
{
// "HTTP Error: 400,
// Configuring database-level settings is not supported."
"collectionGroup": "__default__",
"fieldPath": "*",
"ttl": false,
"indexes": []
}
]
}