Mounting a Searchable Snapshot to the Frozen Tier
Turn a snapshot that already exists in your repository into a queryable, read-only frozen index in a single API call — without restoring it to primary storage and without waiting for a lifecycle phase to age it there.
This is the manual counterpart to the automatic path. Where an ILM searchable_snapshot action mounts an index as it ages into the frozen phase, the mount API lets you promote any snapshot on demand — including one a scheduled backup wrote weeks ago — the moment someone needs to query historical metrics-* data. It is the tool you reach for during an incident review, an audit, or a “what did this dashboard look like in April” investigation, when the data is sitting in cold-archive-repo but not currently mounted anywhere. The mechanics and the two storage modes are explained in full under searchable snapshots and the frozen tier, part of the broader Snapshot Lifecycle Management & searchable snapshots topic; this page is the focused how-to for doing it by hand.
Prerequisites
- Elasticsearch 8.x with at least one
data_frozennode that hasxpack.searchable.snapshot.shared_cache.sizeconfigured — without a shared cache the mount cannot be allocated and hangs. - A snapshot already present in
cold-archive-repo; confirm it withGET _cat/snapshots/cold-archive-repo?vand note the exact snapshot name and the source index name inside it. elasticsearch-pyv8.0+ for theclient.searchable_snapshots.mountsurface.managecluster privilege plusmanageon the target index name for the account performing the mount.
Implementation
A mount is one request against the repository, naming the snapshot, the source index inside it, and how the resulting index should be stored and named. The storage=shared_cache query parameter is what makes this a frozen mount rather than a cold one, and wait_for_completion=true makes the call block until the mount is registered instead of returning while it is still setting up.
POST /_snapshot/cold-archive-repo/snap-2026.04.15/metrics-000017/_mount?wait_for_completion=true&storage=shared_cache
{
"index": "metrics-000017",
"renamed_index": "partial-metrics-000017",
"index_settings": {
"index.number_of_replicas": 0
}
}The path segments matter: cold-archive-repo is the repository, snap-2026.04.15 is the snapshot, and the third segment metrics-000017 is the source index within that snapshot. In the body, index repeats that source-index name and renamed_index gives the mounted index the name it will carry in the live cluster. The partial- prefix is a convention worth following — it mirrors what ILM does for frozen mounts and makes the index instantly recognisable as frozen and only partially resident in _cat/indices output. Dropping number_of_replicas to 0 is the norm on the frozen tier: the repository is already the durable copy, so a lost frozen node re-mounts rather than recovering from a replica, and replicas would only compete for shared-cache space.
For repeatable and unattended mounts, drive the same operation through the Python v8 client. The keyword arguments map one-to-one onto the API path and body, and failures surface as ApiError — a sibling of TransportError, so catch ApiError, not TransportError.
import logging
from elasticsearch import Elasticsearch, ApiError, NotFoundError
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger("frozen_mount")
def mount_to_frozen(es: Elasticsearch, repo: str, snapshot: str, source_index: str) -> str | None:
"""Mount one snapshotted index onto the frozen tier; return the mounted name or None."""
renamed = f"partial-{source_index}"
try:
es.searchable_snapshots.mount(
repository=repo,
snapshot=snapshot,
index=source_index,
renamed_index=renamed,
index_settings={"index.number_of_replicas": 0},
storage="shared_cache", # the frozen tier; "full_copy" would be cold
wait_for_completion=True,
)
logger.info("Mounted %s/%s/%s as %s", repo, snapshot, source_index, renamed)
return renamed
except NotFoundError:
logger.error("No snapshot %s or index %s in repo %s", snapshot, source_index, repo)
return None
except ApiError as exc:
logger.error("Mount failed (%s): %s", exc.status_code, exc.info)
return None
if __name__ == "__main__":
es = Elasticsearch(
"https://es-cluster-01:9200",
api_key="YOUR_BASE64_ENCODED_API_KEY",
verify_certs=True,
request_timeout=60,
)
mounted = mount_to_frozen(es, "cold-archive-repo", "snap-2026.04.15", "metrics-000017")
if mounted:
# The mounted index is immediately queryable, read-only, and served from the shared cache.
count = es.count(index=mounted)["count"]
logger.info("%s holds %s documents", mounted, count)A shared_cache mount registers almost immediately because no bulk download happens up front — segment blocks are pulled from the repository lazily on the first query that needs them. That is why the es.count(...) call right after the mount already works: the metadata is live even though almost none of the data is resident on disk yet.
Verification
First confirm the mounted index exists and is green, and that it is genuinely repository-backed rather than a normal index. The index.store.snapshot settings are the fingerprint of a searchable snapshot:
GET partial-metrics-000017/_settings?filter_path=**.store.snapshot{
"partial-metrics-000017": {
"settings": {
"index": {
"store": {
"snapshot": {
"repository_name": "cold-archive-repo",
"snapshot_name": "snap-2026.04.15",
"index_name": "metrics-000017"
}
}
}
}
}
}Those three values tell you precisely which repository, snapshot, and source index the mount reads from. Next, prove it actually answers queries and confirm it landed on a frozen node — a search against a mounted index touches the shared cache, so a successful hit count is the real end-to-end test:
GET partial-metrics-000017/_search?size=0A hits.total.value matching the archived document count means the mount is serving data through the cache. To confirm the hosting tier, check the node roles — the mount can only run where a frozen node with a shared cache exists:
GET _cat/nodes?v&h=name,node.roleAt least one row’s node.role must include f (data_frozen). If none does, the mount will have stalled during allocation rather than reporting green.
Gotchas and edge cases
- No frozen node, no mount. A
shared_cachemount can only be allocated to adata_frozennode that hasxpack.searchable.snapshot.shared_cache.sizeset. If the deployment has no such node, the mount request may accept but the index stays unassigned indefinitely. Verify withGET _cat/nodes?v&h=name,node.rolebefore mounting, and checkGET _cluster/allocation/explainif the index is stuck. - The mounted index is read-only, permanently. You cannot index, update, or delete documents in
partial-metrics-000017— writes are rejected by a read-only cluster block because the data lives in an immutable snapshot. If you need to modify the data, reindex it into a fresh writable index; there is no setting that unlocks a mounted snapshot for writing. renamed_indexavoids a name clash. If an index calledmetrics-000017still exists live, mounting under the same name would collide. Always giverenamed_indexa distinct name; thepartial-prefix convention keeps mounted indices from ever colliding with their live sources and signals their frozen nature at a glance.- Deleting the mounted index does not delete the snapshot. Removing
partial-metrics-000017withDELETE partial-metrics-000017unmounts it and frees its shared-cache space, but the underlyingsnap-2026.04.15snapshot incold-archive-repois untouched and can be re-mounted later. This is exactly why manual mounting is safe for ad-hoc investigation: mount, query, unmount, and the durable backup is never at risk.
FAQ
Does mounting a snapshot restore its data to disk?
Not with shared_cache storage. The mount registers the index’s metadata in cluster state and leaves the data in the repository, fetching segment blocks on demand into a bounded local cache. Only the blocks a query touches are ever downloaded, which is why a frozen mount is nearly instant and why it can front an index far larger than the node’s disk. A full_copy mount, by contrast, does download the whole index.
Can I mount a snapshot that an SLM backup created?
Yes. Any snapshot in the repository can be mounted, regardless of whether a scheduled SLM policy or an ILM action wrote it. Point the _mount call at the SLM snapshot’s name and the source index inside it. This is a fast way to investigate historical data from a backup without a full restore — just be careful not to let retention delete a snapshot while a mounted index still depends on it.
What happens to the mount if I delete the mounted index?
Deleting the mounted index (for example DELETE partial-metrics-000017) simply unmounts it and releases its shared-cache space. The underlying snapshot in the repository is not affected and remains available to mount again later. Mounting and unmounting are cheap, reversible operations against a durable snapshot that stays put.
Related
- Converting Cold Indices to Searchable Snapshots via ILM — the automatic counterpart, letting ILM mount indices as they age.
- Managing Snapshot Repositories — registering and verifying
cold-archive-repobefore you mount from it.
← Back to Searchable Snapshots & the Frozen Tier · Snapshot Lifecycle Management & Searchable Snapshots