// OSSeva Blog
MigrationPatroni DCS: Moving a Cluster from ZooKeeper to etcd
The short answer
Patroni keeps a PostgreSQL cluster's leader lock, member status and dynamic configuration in a distributed configuration store (DCS). The supported DCS sections are etcd (v2 API), etcd3 (v3 API), Consul, ZooKeeper, Exhibitor and Kubernetes. Patroni's built-in Raft is deprecated since Patroni 3.0.0 (January 2023). The latest release is Patroni 4.1.5, from 12 August 2026. Moving a Patroni cluster from ZooKeeper to etcd is a controlled maintenance operation, not a database migration: the Postgres data stays where it is. The Patroni docs do not publish a dedicated "change DCS" runbook, so the procedure below is built only from behaviour the docs describe, and we mark the one step that is our precaution.
How Patroni uses the DCS
Patroni runs next to PostgreSQL on every node and manages high availability for the cluster: one node is the primary, and the others run as replicas using streaming replication. The DCS is the source of truth. A node may run PostgreSQL as the primary only while it can update the leader lock in the DCS. The lock has a time-to-live (ttl, default 30 seconds). If the leader cannot renew it, the lock expires and the replicas race for it, which is how automatic failover works. Patroni also stores the dynamic configuration there, the settings applied on all cluster nodes, which you change with patronictl edit-config or the REST API.
That design is why the DCS matters so much:
- If you lose the DCS cluster, every Patroni cluster using it goes read-only, unless DCS Failsafe Mode is enabled.
- If the DCS loses its majority, it becomes unresponsive and Patroni demotes the current primary to avoid split-brain.
- DCS or network issues shorter than
retry_timeout(default 10 seconds) do not cause a demotion.
Patroni's FAQ says the separate DCS cluster is the price of making split-brain less likely. One DCS can serve several Patroni clusters, as long as each uses its own namespace and cluster name.
The Patroni DCS options
| Section | Backend | Notes from the docs |
|---|---|---|
etcd3 | etcd, v3 API | Recommended over etcd. Uses etcd's gRPC gateway, so TLS common name authentication is not possible. |
etcd | etcd, v2 API | The FAQ notes v2 is disabled by default from etcd v3.4 and removed in v3.6. |
consul | Consul local agent | Host or URL of the agent; the ACL token needs write on the service, key and session prefixes. |
zookeeper | ZooKeeper ensemble | A list of hosts; SSL needs kazoo 2.6.0 or later; optional ACLs and auth data. |
exhibitor | ZooKeeper via Exhibitor | The host list updates as the ensemble changes. |
kubernetes | Kubernetes API | Leader election through ConfigMaps, or Endpoints with use_endpoints. |
raft | Built-in pysyncobj Raft | Deprecated since 3.0.0 but still documented. |
Why teams move off ZooKeeper, and off Raft
ZooKeeper is a sound DCS, but the ensemble under Patroni is often shared with Kafka, Hadoop or Solr and is frequently an end-of-life version. ZooKeeper 3.4 to 3.7 get no community fixes, and CVE-2023-44981 has no fix for 3.4, 3.5 or 3.6. etcd is the more common choice for new PostgreSQL HA builds. If you move, land on a supported etcd: v3.5, v3.6 and v3.7 are supported, and v3.4 reached end of life with v3.4.45 on 1 June 2026. Teams on the deprecated Raft option have the same reason to move, since the docs no longer recommend it.
What the docs say about pointing Patroni at a new DCS
Three documented behaviours make a DCS move possible:
- A new DCS is recoverable. The FAQ covers a new DCS cluster with different endpoints: update the DCS endpoints in each node's Patroni configuration. Patroni then recreates the status information from the current state of the cluster, and recreates the dynamic configuration from
patroni.dynamic.json, a backup file in each member's Postgres data directory. - Pause mode protects Postgres.
patronictl pauseputs the cluster in maintenance mode and disables automatic failover. While paused, stopping Patroni does not stop the Postgres instance it manages. - Some changes need an agent restart. Local configuration changes are applied with a reload, and environment configuration only at startup. The FAQ points to
patronictl pauseto avoid a failover while restarting agents. - Environment variables win. Values set through environment variables take precedence over the configuration file. If a node sets
PATRONI_ZOOKEEPER_HOSTS, as container images often do, editing the YAML is not enough. The etcd3 equivalents use the same names with ETCD3 in place of ETCD. - bootstrap.dcs does not carry over. Values under
bootstrap.dcsare only used when bootstrapping a fresh cluster. On an existing cluster, the dynamic configuration comes back frompatroni.dynamic.json.
One limit is explicit: keys written with etcd's v2 API are not visible through v3, so you cannot switch from etcd to etcd3 just by editing the config file.
Step by step: ZooKeeper to etcd
- Build the new etcd cluster on a supported release, and use the
etcd3section, notetcd. - Check the Patroni cluster is healthy.
patronictl listshould show one leader and every replica streaming with low lag. Check for environment variables that configure the DCS. Confirmpatroni.dynamic.jsonexists in each data directory, and savepatronictl show-configoutput. - Pause the cluster with
patronictl pause. - Edit each node's Patroni configuration file. Remove the
zookeepersection and addetcd3with the new hosts. Keepscopeandnamespaceunchanged. - Restart the Patroni agents. We restart the primary's agent first, so the first member to write to the empty store is the one already running as primary. That ordering is our precaution, not a documented requirement.
- Verify. Run
patronictl listagainst etcd and check every member, the leader and the timeline. Comparepatronictl show-configwith the saved copy. The pause flag lives in the DCS, so check whether the cluster still shows as paused; if it does not, pause it again before continuing. - Repoint everything else that reads the DCS: patronictl configs on admin hosts, and any tools that watch the leader key directly.
- Resume with
patronictl resume, then test a planned switchover. - Retire the old data. Remove the Patroni znodes from ZooKeeper only after the cluster has run cleanly on etcd. Rolling back is the same procedure in reverse.
# before
zookeeper:
hosts: ['zk1:2181', 'zk2:2181', 'zk3:2181']
# after
etcd3:
hosts: etcd1:2379,etcd2:2379,etcd3:2379
Standby clusters need their own pass. A Patroni standby cluster replicates from another cluster and, per the FAQ, may use a completely independent DCS cluster, so you can move a disaster-recovery site to etcd before or after the primary site.
The same steps apply to a move from Raft, Consul or Exhibitor. Moving to Kubernetes is different, because it normally goes with moving PostgreSQL itself onto Kubernetes.
Checks before you start
- Timing settings must satisfy
loop_wait + 2 * retry_timeout <= ttl; the default value for each is 10, 10 and 30 seconds. Do not tighten them during the move. - Rehearse on a staging Patroni cluster with the same Patroni and PostgreSQL versions.
- If Consul is a candidate, note that Consul 1.17 and later are under the BSL; see Consul end of life.
Where OSSeva fits
Patroni support from OSSeva covers the PostgreSQL clusters, the Patroni layer and the DCS move. For ensembles that stay on ZooKeeper, OSSeva ships patched, signed builds for ZooKeeper 3.4, 3.5, 3.6 and 3.7 today, supports 3.8 and 3.9, and provides VEX attestation for auditors. Start with Patch, or book a discovery call. The Patroni and ZooKeeper page shows how to check which DCS a cluster uses, and etcd extended support covers the other side of the move.
Frequently asked questions
What is the DCS in Patroni?
The distributed configuration store: the key-value store that holds the leader lock, member status and dynamic configuration for a Patroni cluster.
Which DCS should I use with Patroni?
etcd through the etcd3 section is the most common choice. Consul, ZooKeeper and Kubernetes are fully supported. Avoid the deprecated Raft option for new clusters.
Is Patroni's built-in Raft still supported?
It was deprecated in Patroni 3.0.0 (January 2023). It is still documented, but not recommended.
Can I switch from etcd to etcd3 by editing the config?
No. Keys written through the v2 API are not visible through v3, so treat it as a DCS move.
What happens to PostgreSQL if the DCS goes down?
Patroni cannot renew the leader lock, so the primary is demoted and the cluster runs read-only, unless DCS Failsafe Mode is enabled and the primary can reach all members.
Can one DCS serve several Patroni clusters?
Yes. Each Patroni cluster is stored under its own namespace and cluster name, so one etcd or ZooKeeper cluster can hold many, as long as those do not clash.
Can I temporarily disable automatic failover?
Yes. patronictl pause puts the cluster in maintenance mode, and Patroni does not promote a replica node automatically until you run patronictl resume.
What is the latest version of Patroni?
Patroni 4.1.5, released on 12 August 2026.
Tags
Related articles
ZooKeeper Vulnerabilities by Version: CVEs in 3.4 to 3.9
September 29, 2026MigrationZooKeeper Alternatives: ZooKeeper vs etcd, Consul, KRaft and ClickHouse Keeper
September 29, 2026ComplianceWhy Your Scanner Flags the ZooKeeper Inside a Product You Bought, and How VEX Attestation Answers It
September 29, 2026Ready to get your open source under control?
Talk to an OSSeva engineer about CVE coverage, compliance, and migration support for your stack.