If you have ever been in the middle of a database migration and suddenly found your logical replication slots missing, you know how unsettling that feeling is. No obvious error. No warning. Just gone. That is exactly what happened to us while migrating data from a Patroni-managed cluster to a standalone PostgreSQL cluster. What started as a smooth operation turned into a debugging session that taught us something important about how Patroni behaves after a restart.
The Setup
We were running a data migration where the source was a Patroni cluster and the target was a another PostgreSQL cluster. Logical replication was our chosen approach, which meant we had created replication slots on the Patroni side to track the changes flowing out. Things were going well at the start. Data was moving, lag was low, and we felt good about the plan.
Something Went Wrong
At some point, the publisher node on the Patroni side got restarted. After the restart, our logical replication slots were simply gone. This was strange. These were not temporary slots. Temporary slots are expected to disappear after a session ends, but permanent slots are supposed to survive restarts. PostgreSQL itself has no reason to drop them.
We dug into the PostgreSQL logs first. There was nothing. No mention of the slots being dropped, no errors, nothing that pointed to Postgres being the culprit.
The Real Culprit: Patroni
Then we checked the Patroni logs. And there it was. Patroni had dropped our replication slots. After doing more research, we found that this is actually a known behavior. When Patroni restarts PostgreSQL, it goes through a cleanup routine where it drops all replication slots that it does not recognize as its own. Since our replication slots were created manually for the migration and were not registered with Patroni, Patroni treated them as unknown and deleted them.
This behavior exists because Patroni is designed to have full control over the cluster. It does not want stale or unknown slots hanging around, since they can block WAL cleanup and cause disk to fill up. That makes sense from a cluster management perspective, but it can really catch you off guard during a migration.
The Fix: ignore_slots
The solution was to tell Patroni about our replication slots so it would leave them alone. Patroni supports a configuration option called ignore_slots in the config.yml file. You can use this to list the slots Patroni should never touch, even if it does not manage them.
Here is an example of what the configuration looks like:
bootstrap:
dcs:
ignore_slots:
type: logical
Once we added ignore_slots and applied the configuration, Patroni stopped interfering with them. The migration continued without any more surprises.
What to Take Away From This
If you are running logical replication on a Patroni-managed cluster, especially during a migration, always check whether Patroni is aware of your replication slots. Any slot not known to Patroni is at risk of being dropped the next time the node restarts.
The fix is simple once you know about it, but finding it takes time when you are in the middle of a live migration. Hopefully this post saves you that time.

