Cutting Storage Costs with Elasticsearch Searchable Snapshots
Searchable Snapshots let you query cold Elasticsearch indices straight from S3 or other object storage without restoring them to local disk. Here's how mounting works, a worked example, and the read-only and latency trade-offs to plan for.
06 Jul 2025, 17:08 UTC

Every Elasticsearch cluster eventually hits the same wall: the data people actually search is a fraction of the data you're paying to keep on fast disks. Last quarter's logs sit on SSD-backed hot nodes "just in case," and the storage bill climbs while query volume on those indices approaches zero. Searchable Snapshots are Elasticsearch's answer to this: mount a snapshot from cheap object storage (S3, GCS, Azure Blob) as a read-only index, and query it directly without restoring it to local disk first.
The thesis is simple: if an index is read-heavy, rarely queried, and never updated, you can stop paying hot-tier prices for it and still keep it searchable.
How a mounted snapshot actually works
Normally, restoring a snapshot means copying every shard back onto cluster nodes before the index can serve queries. A searchable snapshot skips that. When you mount one, Elasticsearch exposes the snapshot's contents as an index and fetches data blocks from the repository on demand. Only the blocks a query touches are pulled over the network; a local cache on the node holds recently used blocks so repeat queries don't go back to the repository every time.
Two consequences follow from that design. First, RAM and local disk usage stay low compared with a fully restored index. Second, the first query against cold data pays a network round trip and decompression cost, so latency is higher than a local shard — often noticeably so on the first hit, less so once the cache warms up.
The feature has been available since Elasticsearch 7.10 and requires the repository plugin for your object store (for example, the S3 repository plugin). One hard rule: the cluster mounting the snapshot must run the same Elasticsearch version that created it. Cross-version mounts are not supported, which matters when you upgrade clusters and expect old snapshots to remain mountable.
A worked example: mounting cold logs from S3
Assume a cluster on Elasticsearch 8.x with the S3 repository plugin installed, and an index logs-2026-01 that's closed and no longer written to. All commands below run against the cluster's REST API (via curl or Kibana Dev Tools) with a user that has manage cluster privileges.
First, register the repository. Replace the bucket and region with your own, and make sure node-level credentials (IAM role, keystore settings) are already in place:
PUT /_snapshot/cold_repo
{
"type": "s3",
"settings": {
"bucket": "my-es-snapshots",
"region": "us-east-1"
}
}Take a snapshot of the cold index and wait for it to finish:
PUT /_snapshot/cold_repo/snap-logs-2026-01?wait_for_completion=true
{
"indices": "logs-2026-01",
"include_global_state": false
}Then mount it as a searchable snapshot index. In 8.x this is done through the mount API:
POST /_snapshot/cold_repo/snap-logs-2026-01/_mount
{
"index": "logs-2026-01",
"renamed_index": "logs-2026-01-cold"
}After the mount returns, run a query against logs-2026-01-cold and compare it with the same query on the original index before you delete it. Check GET logs-2026-01-cold/_stats and a few realistic queries to confirm results match and latency is acceptable for your use case. Only then delete the local index.
Letting ILM do the moving
You rarely want to do this by hand for every index. Index Lifecycle Management (ILM) supports searchable snapshots directly: in the cold or frozen phase, the searchable_snapshot action snapshots the index, deletes the local copy, and mounts the snapshot automatically. A minimal cold phase looks like this:
"cold": {
"min_age": "30d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "cold_repo"
}
}
}This is where the cost story gets real: indices roll to object storage on a schedule, and nobody has to remember to mount anything.
The trade-offs you need to accept
The biggest constraint is that mounted indices are read-only. Any attempt to index or update documents into them fails. If the data changes, you re-index into a regular index — so this pattern fits logs, metrics, and archives, not anything mutable.
Second, latency is bounded by your repository's network throughput. Against S3 in the same region this is usually fine for occasional queries; against a low-bandwidth on-prem repository it can be painful. Measure with your real queries before committing.
Third, consistency is fixed at snapshot time. Changes to the source data after the snapshot aren't visible until you take and mount a new one. And operationally, a missing or misconfigured repository plugin means mounts simply won't work — verify the plugin is installed on every node that will hold mounted shards.
A practical way to decide
Pick one genuinely cold index, snapshot it, mount it alongside the original, and run your ten most common queries against both. If the latency difference is acceptable and nobody needs to write to the data, wire up an ILM policy and start reclaiming your hot disks. Keep the original index until you've verified the mounted copy returns identical results — the mount is cheap to delete, a deleted index is not.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.