ntfy v2.32.0 keeps passive cluster nodes out of maintenance work
The new release keeps passive nodes from competing over maintenance tasks in a shared database.
The difficult part is not publishing messages
Running several ntfy instances behind a reverse proxy looks straightforward when they share a PostgreSQL database. The complication comes from background work that is not tied to a single request: removing expired messages, attachments and tokens, or updating metrics. If every node performs those jobs independently, the database receives duplicate work and one replica can overwrite state calculated by another.
ntfy server v2.32.0 addresses an important part of that experimental setup. Shared-database maintenance jobs now run only on the cluster leader. That makes it possible to keep a passive node around without letting it interfere with cleanup work or the published-message total.
The release was published on October 18, 2026. It also stores the oldest schema version each database store can work with, so an older ntfy version can refuse to start when a newer release has applied an incompatible migration. That is a sensible safeguard against an old instance reading migrated data incorrectly.
Where this fits in a homelab
For me, ntfy works best as a general-purpose notification layer. A cron job can report a completed backup, Docker Compose can report a failed startup, and a Laravel application can send an alert through a regular HTTP request. It does not need to become the whole observability stack to be useful; important messages simply need to reach my phone or desktop.
One ntfy instance is usually enough for a small setup. But when PostgreSQL, a reverse proxy and multiple machines are already part of the homelab, spreading client connections across several nodes becomes interesting. A passive node has specific uses, such as test clients or an instance that should stay outside the normal load-balancer pool.
The documentation describes a passive node as one that cannot become leader for singleton cluster jobs and never becomes ready for load-balanced traffic. It is therefore not a drop-in high-availability standby for an active node. Its role is deliberately narrow, and the network design should reflect that.
A minimal cluster configuration
ntfy clustering is still labelled experimental and requires PostgreSQL through database-url. Every node needs a stable identifier, a private address for node-to-node traffic and a shared secret. The cluster listener must not be exposed to the public internet.
This example follows the documented configuration for an active node. Other nodes need their own identifier, private listener address and advertised URL, while the secret must match on every member. A node that stays outside the load balancer can use the passive role.
base-url: "https://ntfy.example.com"
database-url: "postgres://ntfy:password@postgres:5432/ntfy"
experimental-cluster-node-id: "ntfy-1"
experimental-cluster-listen: "10.0.0.5:2587"
experimental-cluster-advertise-url: "http://10.0.0.5:2587"
experimental-cluster-secret: "a-long-shared-secret"
experimental-cluster-role: "active"The load balancer should check /v1/ready, not /v1/health. That distinction matters because a passive node never reports itself as ready for the traffic pool. The health endpoint only covers the node’s own database heartbeat, not whether all node-to-node links are working correctly.
Straightforward alerts from scripts and applications
ntfy remains practical because its HTTP API is simple. A cron script or a container can publish to a topic with curl, without a dedicated SDK or another queue in the middle. Laravel can use the same approach from a custom notification, an event listener or a scheduled command through its HTTP client.
Here is a basic notification for a successful backup. The Title header is optional, but it makes alerts easier to scan when several services publish to the same topic. On a self-hosted server, I would replace the domain and protect the topic with ntfy access controls.
curl --fail \
-H "Title: Backup completed" \
-d "The nightly backup completed successfully." \
https://ntfy.example.com/infra-alertsKeeping topics separated by purpose preserves context. I would use different topics for infrastructure, backups, application failures and home automation instead of sending everything into one noisy channel. It also reduces the chance that a minor notification hides something that needs action.
The fine print
This release does not make experimental clustering production-ready. ntfy’s own documentation still marks the experimental-cluster-* options as unsuitable for production use. If ntfy is the only alerting path for a critical service, multiple instances should not be treated as a guarantee that all operational risks are gone.
Limits are enforced per node. In a cluster, a client spreading requests across several nodes can consume each node’s limits separately. Messages published with cache: no also have weaker cross-node delivery guarantees because they are not stored in the shared database.
The private network between nodes needs attention as well. If cluster traffic works in one direction but fails in the other, messages may arrive later through database reads even while the nodes still look healthy. The ntfy cluster status command helps inspect what each node sees from its peers and is useful for catching that kind of asymmetric failure.
Version 2.32.0 is a worthwhile update for anyone already testing the PostgreSQL-backed cluster and needing a passive node that does not compete for maintenance work. A typical home installation does not need to be redesigned around it. The benefit is more modest and more useful: fewer opportunities for replicas to race over shared background tasks.