Elasticsearch cross-cluster replication (CCR) keeps a follower index current with a leader index in another cluster, which makes it useful for disaster recovery, regional read locality, and controlled maintenance windows without redirecting writes away from the primary workload too early.
CCR is started on the follower cluster after a remote-cluster alias already exists for the leader cluster. The follower index is created with the _ccr/follow API, Elasticsearch copies the current Lucene segments with remote recovery, and then the follower continues pulling new operations from the leader while preserving the leader mappings and most index settings.
Current self-managed deployments commonly connect remote clusters with API-key authentication over the dedicated remote-cluster interface on port 9443, although certificate-based remotes can still use the transport interface on 9300. The local and remote clusters must keep compatible versions and the same license tier that supports CCR, nodes that initiate replication must retain the remote_cluster_client role, and cluster-level objects such as templates, ILM policies, ingest pipelines, and users are not replicated automatically.
Steps to set up Elasticsearch cross-cluster replication:
- Confirm the current CCR prerequisites on both clusters.
All local master nodes plus at least one local data node must keep the remote_cluster_client role, and both clusters must use the same license type with CCR support. On secured clusters, configure the required replication privileges before continuing: the follower side needs manage_ccr plus monitor, read, write, and manage_follow_index on the follower index, while the leader workload needs read_ccr plus monitor and read access for the leader index. When the remote connection uses a cross-cluster API key, those leader-side permissions are carried by the key on the local cluster instead of a remote user role. If the remote-cluster alias still needs to be created or changed, that separate cluster-settings workflow also requires the cluster manage privilege on the local cluster.
- Confirm the follower cluster can reach the configured remote-cluster alias.
$ curl --silent --show-error --user elastic:strong-password "https://follower-es.example.net:9200/_remote/info?pretty&filter_path=*.connected,*.mode,*.seeds,*.proxy_address,*.num_nodes_connected,*.cluster_credentials" { "dr-site" : { "connected" : true, "mode" : "sniff", "seeds" : [ "remote-es.example.net:9443" ], "num_nodes_connected" : 1, "cluster_credentials" : "::es_redacted::" } }connected must be true before the follower can start. API-key remotes usually report cluster_credentials and use the remote-cluster server port 9443, while certificate-based remotes typically connect over the transport port 9300.
- Confirm the leader index exists on the leader cluster.
$ curl --silent --show-error --user elastic:strong-password "https://leader-es.example.net:9200/_cat/indices/logs-2026.04.02?v&s=index" health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size green open logs-2026.04.02 7PwqJx0NQe6yM5M0VVnK6A 1 1 5 0 33.1kb 16.5kb 16.5kb
Run the command against the leader cluster, not the follower cluster. On secured clusters, the request needs access that can read and monitor the leader index.
- Create the follower index on the follower cluster with the _ccr/follow API.
$ curl --silent --show-error --user elastic:strong-password --header "Content-Type: application/json" --request PUT "https://follower-es.example.net:9200/logs-follow/_ccr/follow?pretty&wait_for_active_shards=1" --data '{ "remote_cluster": "dr-site", "leader_index": "logs-2026.04.02" }' { "follow_index_created" : true, "follow_index_shards_acked" : true, "index_following_started" : true }Direct writes to the follower index are rejected. Send ingest traffic to the leader index only.
If the initial remote recovery is still copying existing segment files, the follower can remain paused briefly after the API call before it switches to active replication.
- Check the follower state with the _ccr/info API.
$ curl --silent --show-error --user elastic:strong-password "https://follower-es.example.net:9200/logs-follow/_ccr/info?pretty&filter_path=follower_indices.*.follower_index,follower_indices.*.remote_cluster,follower_indices.*.leader_index,follower_indices.*.status" { "follower_indices" : [ { "follower_index" : "logs-follow", "remote_cluster" : "dr-site", "leader_index" : "logs-2026.04.02", "status" : "active" } ] }active confirms the follower has finished any initial pause and is pulling operations from the leader. If the status remains paused, wait for remote recovery to finish or inspect the local cluster logs for follow-task errors.
- Check follower replication metrics with the _ccr/stats API.
$ curl --silent --show-error --user elastic:strong-password "https://follower-es.example.net:9200/logs-follow/_ccr/stats?pretty&filter_path=indices.*.index,indices.*.shards.*.shard_id,indices.*.shards.*.operations_read,indices.*.shards.*.operations_written,indices.*.shards.*.time_since_last_read_millis,indices.*.shards.*.fatal_exception" { "indices" : [ { "index" : "logs-follow", "shards" : [ { "shard_id" : 0, "operations_read" : 5, "operations_written" : 5, "time_since_last_read_millis" : 184, "fatal_exception" : null } ] } ] }Growing operations_read and operations_written counters indicate the follower is consuming leader changes. A non-null fatal_exception means replication stopped for that shard and needs investigation before the follower can catch up.
- Index a test document into the leader index on the leader cluster.
$ curl --silent --show-error --user elastic:strong-password --header "Content-Type: application/json" --request POST "https://leader-es.example.net:9200/logs-2026.04.02/_doc/ccr-smoke-test-01?refresh=true" --data '{ "message": "ccr smoke test", "service": "orders", "@timestamp": "2026-04-02T08:15:00Z" }' { "_index" : "logs-2026.04.02", "_id" : "ccr-smoke-test-01", "_version" : 1, "result" : "created", "forced_refresh" : true, "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 6, "_primary_term" : 1 } - Search the follower index for the replicated document from the follower cluster.
$ curl --silent --show-error --user elastic:strong-password --header "Content-Type: application/json" --request POST "https://follower-es.example.net:9200/logs-follow/_search?pretty" --data '{ "query": { "ids": { "values": ["ccr-smoke-test-01"] } } }' { "took" : 2, "timed_out" : false, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "hits" : [ { "_index" : "logs-follow", "_id" : "ccr-smoke-test-01", "_source" : { "message" : "ccr smoke test", "service" : "orders", "@timestamp" : "2026-04-02T08:15:00Z" } } ] } }Replication is asynchronous, so allow a short delay before retrying the search. When leader indices roll over continuously, use auto-follow patterns instead of creating each follower index by hand.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.