Skip to content

CONCEPT Cited by 1 source

so_reuseport for PgBouncer scaling

Definition

SO_REUSEPORT is a Linux socket option (kernel ≥ 3.9) that allows multiple processes to bind to the same (address, port) tuple. The kernel then distributes incoming connections across the listening processes via a hash of the 4-tuple (src-ip, src-port, dst-ip, dst-port), giving approximate load balancing with zero user-space coordination.

PgBouncer exposes this via its so_reuseport config flag. Because PgBouncer is a single-threaded, event- loop process by design, enabling so_reuseport and starting N PgBouncer processes is the canonical way to get PgBouncer to use more than one CPU core on a single machine.

Why PgBouncer needs it

  • PgBouncer is single-process, single-threaded — a deliberate simplicity choice.
  • A single PgBouncer process saturates at one core's worth of CPU.
  • Scaling up on the same host requires multiple independent PgBouncer instances.
  • Without SO_REUSEPORT, they'd need distinct ports and the client would need to know about all of them.
  • With SO_REUSEPORT, clients connect to a single port and the kernel distributes across the N PgBouncer instances transparently.

Seen in

  • sources/2020-06-23-zalando-pgbouncer-on-kubernetes-minimal-latency — Zalando's benchmark runs two PgBouncer instances with so_reuseport on the same host to explore how CPU placement (real core vs hyperthread) affects latency. Kukushkin's framing: "Two instances of PgBouncer will run with so_reuseport option, which is essentially a way to get PgBouncer to use more CPU cores."

Caveats

  • Kernel hash balancing is per 4-tuple, not per packet — long-lived client connections stay pinned to the same PgBouncer process. Uneven connection counts → uneven load.
  • On Kubernetes, you usually don't need SO_REUSEPORT: scaling is horizontal (more pods) rather than vertical (more processes per pod). SO_REUSEPORT becomes relevant when a pod has multiple CPUs and you want to saturate them without scaling out pod replica counts.
Last updated · 476 distilled / 1,218 read