I currently run Pi-hole and Unbound in k3s as a single-replica Deployment. I'd like to move to three replicas and perform staged or rolling upgrades. Should I use a StatefulSet with one persistent volume per pod, or can I safely scale the existing Deployment? I want to avoid database or configuration corruption while preserving settings, statistics, and blocklists.
3 Answers
You may not need a StatefulSet just to perform a staged rollout. A Deployment can run three replicas with a RollingUpdate strategy, for example maxSurge: 1 and maxUnavailable: 1. That replaces pods gradually. However, this is only safe if the replicas do not share a writable database or volume.
A StatefulSet makes more sense when every pod needs its own persistent volume and stable identity. Sharing one volume between Pi-hole replicas could corrupt its database, while separate volumes mean each instance keeps its own data.
Pi-hole is not especially well suited to running as replicated stateful workloads. If you do not need its UI and local database, consider a more declarative combination such as CoreDNS for local records and Blocky for filtering. Each pod can independently read configuration and blocklists from a shared Git, OCI, or HTTP source, making horizontal scaling and rolling upgrades much simpler. A separate Redis-compatible cache can optionally be shared by the DNS services.
Pi-hole’s database and configuration model make active-active replication complicated. You would need an external synchronization mechanism, such as Gravity Sync, and should be careful during upgrades because different versions might use different database schemas. Pause synchronization while changing versions, then verify that the instances converge afterward.
An external sync tool can help keep blocklists and settings aligned, but it does not automatically eliminate conflicts if two instances modify the same data at once.

The main concern is making sure multiple pods never write to the same database at the same time.