The Kubernetes Connection Multiplier: Size YSQL Before Your Pods Scale

A connection pool can look perfectly reasonable when you inspect one application pod.

Ten connections? Fine.

Twenty? Maybe still fine.

But Kubernetes does not stop at one pod.

When the Horizontal Pod Autoscaler starts creating replicas, it also creates more copies of every client-side connection pool. Suddenly, what looked like a small local setting becomes a database-wide capacity problem.

That leads to a simple but important question:

  • Have you multiplied your maximum pod count by the connection pool size?

If not, that calculation is worth doing before your busiest day does it for you.

💡 The Core Problem
A client-side pool such as HikariCP controls connections for one application instance.
Kubernetes can create dozens of those instances.
The database has to handle the combined connection demand from all of them.

The Hidden Multiplier

Suppose you have several Kubernetes workloads connecting to the same YugabyteDB cluster.

The connection math is:

Maximum Client Connections
=
Σ (Maximum Pods × Connections per Pod)

If a pod creates more than one independent database pool, include those too.

The important word here is maximum.

Sizing against the three pods you normally run at 2:00 AM does not help when the autoscaler is allowed to create forty during a traffic spike.

Here’s an illustrative example:

Workload Maximum Pods Pool per Pod Potential Connections
Checkout API 36 10 360
Inventory API 16 8 128
Background Workers 8 6 48
Maximum Open Client Connections 536

Nothing about those individual pool sizes looks especially alarming.

Together, though, the application fleet can ask YugabyteDB to maintain 536 client connections.

Now Compare That With Backend Capacity

Without connection pooling at the database layer, PostgreSQL-style client connections are expensive because each server connection is backed by a PostgreSQL backend process.

YugabyteDB’s current sizing guidance (Right-size the cluster) recommends no more than about 15 server connections per vCPU as a baseline, and explicitly notes that latency-sensitive or high-throughput workloads may need fewer because CPU context switching increases as active connection counts grow.

Suppose the YugabyteDB cluster has:

3 YB-TServers
×
8 vCPU per node
24 Total vCPU

At 15 server connections per vCPU:

24 vCPU × 15 connections per vCPU
=
360 Server Connections

A more conservative workload might plan around 10 active connections per vCPU:

24 vCPU × 15 connections per vCPU
=
360 Server Connections

Now compare that with the 536 possible application connections.

Planning View Backend Connections 536 Client Connections vs. Budget
15 connections / vCPU 360 1.49× the baseline limit
10 connections / vCPU 240 2.23× the planning budget

The point is not that 10 or 15 is universally correct.

The point is that you need to compare:

  • How many client sessions can my application create?

against:

  • How many active server backends should this cluster actually support?

Why Shrinking Every Client Pool Isn’t a Complete Solution

The obvious reaction is:

  • Fine. I’ll just make every pool smaller.

That helps, but it has a limitation.

A pool inside one application pod has no awareness of:

  • ● how many other pods exist,
  • ● how many other microservices are connecting,
  • ● whether another workload is scaling,
  • ● or how many physical PostgreSQL backends the database should support.

Each pool enforces a local limit against a global database constraint.

That is the architectural mismatch.

One Pod
↓
Local Client Pool
↓
Cannot See Every Other Pool

YSQL Connection Manager Changes the Equation

YugabyteDB includes YSQL Connection Manager (YCM), a built-in server-side connection pooler.

When enabled, applications hold logical client connections, while YCM manages a smaller pool of physical PostgreSQL server connections underneath them.

Conceptually:

YSQL Connection Manager decouples:
Logical Side
Application Sessions
↔
Decoupled
Physical Side
PostgreSQL Backend Processes
Many logical application sessions can share a smaller pool of physical PostgreSQL backends.

That is the important architectural change.

The Three Numbers I Would Write on the Whiteboard

When evaluating a Kubernetes application against YugabyteDB, I would collect these three numbers:

1️⃣ Maximum Client Sessions
Maximum pods × pool size, summed across every workload.
2️⃣ Concurrent Active Work
How many sessions will actually be executing transactions at the same time?
3️⃣ Physical Backend Budget
How many active PostgreSQL backends should the cluster support while meeting latency goals?

If #1 is much larger than #2, YCM has a lot of opportunity to multiplex sessions.

If #2 approaches #3, then the problem is no longer idle connection overhead.

It is actual query concurrency.

YCM Does Not Make Active Queries Free

This is an important distinction.

YCM can allow many idle or intermittently active client sessions to share fewer physical server connections.

But a client actively executing work needs a physical backend.

YugabyteDB’s YCM metrics documentation makes this explicit: the number of active logical connections equals the number of active server connections.

For example:

Many Sessions, Limited Active Work
536 client sessions open
72 transactions actively executing

That does not mean YugabyteDB needs 536 active PostgreSQL backends.

At that moment, only the 72 active logical connections need to be attached to active physical server connections. The remaining client sessions can stay open without each permanently owning its own PostgreSQL backend.

Now compare that with:

Many Sessions, All Actively Executing
536 client sessions open
536 transactions actively executing

That is a very different workload.

If all 536 logical connections are active at the same time, YCM needs 536 active server connections to service them concurrently. At that point, the limiting factor is no longer the number of idle client sessions, it is the amount of real concurrent database work the cluster is being asked to execute.

⚠️ Connection Concurrency ≠ Query Concurrency
YSQL Connection Manager is extremely useful when many clients are idle, waiting, or intermittently active.
If hundreds of sessions are all active at the same time, those sessions still need physical backend execution capacity.

Enable YSQL Connection Manager

For a local yugabyted environment, you can enable YCM with:

				
					./bin/yugabyted start \
  --tserver_flags="enable_ysql_conn_mgr=true" \
  --ui false
				
			

When enabled, each YB-TServer starts a YSQL Connection Manager process along with PostgreSQL.

For YugabyteDB Anywhere, use the platform’s Connection Pooling configuration rather than manually setting enable_ysql_conn_mgr, ysql_conn_mgr_port, or pgsql_proxy_bind_address.

First, Prove You’re Actually Using YCM

Before doing any connection-count testing, verify the session is really going through Connection Manager.

One quick check is:

				
					SHOW yb_is_client_ysqlconnmgr;
				
			

Expected:

				
					.yb_is_client_ysqlconnmgr
--------------------------
 on
				
			

If it returns off, stop there. You are not measuring YCM behavior.

🔎 Related YugabyteDB Tip
Want several quick ways to prove that your session is actually going through YSQL Connection Manager?

Don’t Just Count Connections – Watch Their State

YCM exposes per-pool statistics at:

				
					http://<YB-TServer>:13000/connections
				
			

The current metrics distinguish between logical clients and physical servers, including:

Metric What It Tells You
active_logical_connections Client sessions currently attached to server connections and executing transactions.
waiting_logical_connections Open clients that are currently idle or waiting.
queued_logical_connections Clients waiting to obtain a physical server connection.
active_physical_connections PostgreSQL backends currently attached to logical clients.
idle_physical_connections Backends sitting in the server-side pool ready for reuse.
avg_wait_time_ns How long clients are waiting to acquire a server connection.
sticky_connections Sessions that cannot freely return their backend to the shared pool.

These metrics are also exposed through Prometheus.

Here’s the Pattern I Would Watch For

✅ Pooling Is Helping
Many logical clients are open.
Many are waiting or idle.
Physical backend count remains substantially lower.
⚠️ Backend Pressure Is Real
Active logical connections keep rising.
Queued clients and wait time increase.
Active physical connections approach the backend budget.

The second situation is not evidence that YCM failed.

It means your workload is consuming real backend execution capacity.

One More Multiplier: Connection Distribution

There is another subtlety.

YCM runs on each YB-TServer, and YugabyteDB’s current best-practice guidance recommends combining Connection Manager with Smart Drivers so connections are distributed efficiently across nodes.

A cluster-wide calculation such as:

3 Nodes × 8 vCPU × 15 Connections per vCPU
=
360 Server Connections

assumes the workload is reasonably balanced.

If most client sessions land on one YB-TServer, that node can encounter connection or CPU pressure while other nodes still have room.

💡 Think Per Node Too
A healthy cluster-wide connection total can still hide an overloaded individual YB-TServer.
Size the cluster globally, but verify that application connections and active work are also distributed across the nodes.

What About Sticky Connections?

Not every logical connection is equally reusable.

Session state, temporary objects, and other features can cause a client connection to become sticky, meaning its physical backend cannot immediately return to the shared pool.

YugabyteDB recommends reducing stickiness where possible to maximize server-connection reuse.

✅ Great Multiplexing Opportunity
Lots of logical clients
+
Short transactions
+
Little stickiness
More backend reuse
⚠️ Less Multiplexing Opportunity
Lots of logical clients
+
Long transactions
+
Sticky state
Much less backend reuse

Client Pools and YCM Are Not Competitors

You do not necessarily have to throw away HikariCP, Tomcat pooling, or another application-side pool just because YCM exists.

They solve different problems.

Layer Primary Job
Application-Side Pool Reuse connections inside an application process and avoid constantly creating new network/database sessions.
YSQL Connection Manager Share physical server backends across logical client sessions at the database layer.

YugabyteDB’s client best-practice documentation specifically discusses using popular application pools together with YugabyteDB Smart Drivers and YSQL Connection Manager.

A 60-Second Preflight Check

Before deploying an autoscaling application, answer these questions:

🧮 Kubernetes Connection Preflight
1. What is the maximum replica count for every service?
2. How many client connections can each pod hold?
3. What is the total maximum logical connection count?
4. How many transactions are expected to be active concurrently?
5. How many physical YSQL backends can the cluster support while still meeting latency targets?
6. Is YSQL Connection Manager enabled and verified?
7. Are connections balanced across YB-TServers?

If you do that math before production traffic arrives, a connection spike becomes a capacity-planning exercise instead of an outage investigation.

Related YugabyteDB Tips

These earlier Tips go deeper into specific parts of this topic:

YugabyteDB Tip Why Read It?
Right-Sizing Connections with YCM Goes deeper into logical versus physical connections and sizing backend capacity.
Why Active Queries Still Need Backend Connections Explains why YCM cannot multiplex away genuinely concurrent active SQL work.
Am I Actually Using YCM? Shows several quick ways to verify that your client session really is going through Connection Manager.
Safely Use Dynamic Tenant GUCs with RLS and YCM Covers an important session-state consideration when using YCM in multi-tenant applications.

Final Takeaway

Kubernetes makes scaling an application easy.

It also makes it very easy to multiply database connections without realizing it.

A pool size that looks harmless on one pod can become hundreds or thousands of sessions once several independently autoscaling services are added together.

YSQL Connection Manager changes the architecture by separating logical client sessions from physical PostgreSQL backends.

But there is one number it cannot make disappear:

  • actual concurrent work.
💡 The Key Point
🧮 Do the Multiplication
Maximum pods × pool size tells you how large the application’s connection footprint can become.
🔀 Separate Sessions from Backends
YCM allows many logical clients to share a smaller pool of physical server connections.
⚙️ Size for Active Work
Physical backend capacity should ultimately be driven by concurrent active SQL and your latency goals.
Don’t size the database for how many pods you normally have. Size the connection architecture for how many pods Kubernetes is allowed to create.

Have Fun!

My wife woke up, looked at me, and said, “Happy Anniversary!”  Uh-oh. 😳 Between our cross-country move and an upcoming business trip to D.C., I had completely forgotten. My brain had apparently already been packed in a box.

Luckily, she was coming along and I managed to sneak a visit to the Smithsonian’s National Zoo into the trip so she could see the pandas. She LOVES pandas. 🐼 Crisis averted. Phew!

Here’s Qing Bao enjoying her bamboo, blissfully unaware she just helped save a husband’s hide. 😂