Configuring Cloud SQL Auth Proxy Connection Limits

This guide is part of GCP Cloud SQL Connection Pooling, and it targets one specific incident: your application starts failing with FATAL: remaining connection slots are reserved for non-replication superuser connections or FATAL: sorry, too many clients already even though connections are flowing through the Cloud SQL Auth Proxy. The instinct is to blame the proxy. The proxy is almost never the cause. Because the Auth Proxy passes connections through one-for-one, the ceiling you are hitting is the instance’s max_connections, and the proxy’s --max-connections flag is a downstream safety valve that, when set wrong, either masks the problem or refuses traffic prematurely. This guide diagnoses which ceiling you actually hit and sizes both correctly.

A typical log signature looks like this — note that the error originates from the database server, not the proxy:

2026-06-20T14:22:08Z ERROR connection to server failed:
  FATAL:  remaining connection slots are reserved for non-replication superuser connections (SQLSTATE 53300)
2026-06-20T14:22:09Z [cloud-sql-proxy] WARNING: max connections (50) reached, refusing new connection

The first line is the instance saying it is full. The second is the proxy’s own valve tripping. They are different ceilings, and confusing them is what turns a five-minute fix into an afternoon.

Rapid incident diagnosis

Work from the database outward, because the instance ceiling is authoritative and the proxy ceiling is something you configured.

  1. Confirm the instance is full. A 53300 SQLSTATE or too many clients already comes from Postgres/MySQL itself. Run SELECT count(*) FROM pg_stat_activity; and compare to SHOW max_connections;. If count(*) is at or just below the ceiling (minus superuser_reserved_connections), the tier is exhausted — the proxy is innocent.
  2. Check whether the proxy refused first. Grep the proxy logs for max connections / refusing new connection. If the proxy’s --max-connections is lower than what the instance could accept, the proxy is the bottleneck and the database never saw those connections. This is starvation imposed by the valve, not exhaustion of the tier.
  3. Distinguish exhaustion from a leak. If pg_stat_activity shows many connections in idle in transaction, you have a client-side leak, not a sizing problem — the pool is opening backends it never returns. Fix the leak before raising any ceiling.
  4. Attribute the load. Group pg_stat_activity by application_name/usename to find which replica fleet or rogue job is consuming the slots. A migration job or a misconfigured replica with an oversized pool is a frequent culprit.

The decision is binary: if the proxy refused before the instance filled, raise (or remove) the proxy flag; if the instance filled, the proxy flag is irrelevant and you must reduce the client pool fan-in or raise the tier.

What max-connections actually bounds The flag caps how many client connections the Auth Proxy will accept. Each accepted connection still opens its own backend, so the flag is a safety valve rather than a multiplexing control. client connections 100 from the pods sharing this proxy Auth Proxy max-connections = 80 20 clients refused at the proxy 80 pass through, one backend each Cloud SQL 80 backends exactly the number that got through The flag protects the database from overshoot; it does not reduce the backend count for accepted connections Set it just below your share of max_connections so overshoot fails at the proxy with a clear error rather than at the database
The flag is a circuit breaker rather than a pool: it decides how many client connections are accepted, and every accepted one still consumes a backend.

Mathematical sizing / parameter formula

The Auth Proxy does not pool, so the total backend processes equal the sum of every client connection forwarded through every proxy. The governing inequality is the same fan-in budget used across GCP Cloud SQL Connection Pooling:

Σ (per_replica_pool_size × replica_count)
  + superuser_reserved_connections
  + admin / migration / monitoring sessions
  ≤ instance max_connections × 0.85          (keep ~15% headroom)

The proxy’s --max-connections should be set to the per-proxy share of that budget, acting as a circuit breaker so a single runaway replica cannot starve the others:

proxy --max-connections  ≈  per_replica_pool_size  + small_burst_margin

If the proxy is a sidecar (one proxy per replica), set --max-connections to roughly the replica’s pool_size plus a few connections of margin for health checks and short-lived admin connections. If the proxy is a shared gateway (one proxy serving several apps), set it to the aggregate budget those apps are allowed.

max_connections itself is derived by Cloud SQL from the machine tier’s memory. Approximate Postgres defaults — always confirm with SHOW max_connections:

Machine tier (vCPU / memory) Approx. default max_connections Suggested per-sidecar-proxy --max-connections
Shared-core (1 vCPU / 0.6–1.7 GB) 25 – 50 5 – 10
1 vCPU / 3.75 GB ~100 ~15
2 vCPU / 8 GB ~200 ~25
4 vCPU / 16 GB ~400 ~25 (pool 20 + margin)
8 vCPU / 32 GB ~800 ~40
16 vCPU / 64 GB+ ~1000+ (flag-capped) ~50

Worked example. A 4 vCPU / 16 GB instance reports max_connections = 400. You run 12 replicas, each with an Auth Proxy sidecar. Reserve 3 superuser slots, 15 admin/migration connections, and 15% headroom (60). App budget = 400 − 3 − 15 − 60 = 322, so 322 / 12 ≈ 26 per replica. Set each replica’s client pool to 20 and each sidecar proxy’s --max-connections to 25 (pool size plus a 5-connection margin). Total worst case = 12 × 25 = 300 < 322. The proxy valve guarantees no single replica can exceed 25 even during a bug, protecting the instance ceiling.

Exact remediation & configuration

Set the proxy ceiling on the sidecar and harden the client pool so the two agree. The proxy flag is a guardrail; the client pool size is the real control.

# Auth Proxy sidecar — the --max-connections valve
- name: cloud-sql-proxy
  image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:latest
  args:
    - "--private-ip"
    - "--auto-iam-authn"
    - "--max-connections=25"     # per-sidecar valve: pool_size (20) + margin (5)
    - "--max-sigterm-delay=30s"  # drain in-flight conns on shutdown, avoid TCP resets
    - "project:region:instance"

If the instance itself is the ceiling and the workload genuinely needs more backends, raise the tier (preferred — more memory per backend) or raise the max_connections database flag with caution:

# Raise the instance ceiling — only if the tier's memory can support it
gcloud sql instances patch INSTANCE_NAME \
  --database-flags max_connections=600
# This triggers a restart. Verify memory headroom first; each backend costs RAM.

For zero-downtime application of a smaller client pool (the safer fix), roll the deployment with the reduced pool_size/maximumPoolSize rather than patching the instance. Reducing per-replica pool size never requires an instance restart and takes effect as replicas cycle. If you must raise max_connections, schedule it during a maintenance window because the flag change restarts the instance.

When the 1:1 model simply cannot fit the workload, stop scaling the proxy valve and introduce a real pooler: PgBouncer in transaction mode in front of the proxy, as described in GCP Cloud SQL Connection Pooling. The proxy’s --max-connections then governs only PgBouncer’s small backend pool.

Auth Proxy deployment shapes Running the proxy as a sidecar gives each pod its own instance and its own limit, while a shared deployment applies one limit across the fleet and is the only shape where the flag bounds the total. sidecar per pod lowest latency, no shared failure limit applies per pod total = limit × pods the flag bounds nothing globally shared deployment one limit across the whole fleet limit applies to everything total = limit but it is now a shared dependency connector library no separate process at all no connection limit flag total = application pool size the pool is the only bound Only the middle shape makes the flag a fleet-wide bound — and it introduces a component whose availability now matters. In every shape, the application pool size remains the number that actually determines the backend count.
The flag's scope depends entirely on deployment shape. Only a shared instance makes it a fleet-wide bound, and in every shape the application's pool size is what determines demand.

Validation & verification

After applying, confirm both ceilings from the database side and the client side.

-- Instance ceiling and current usage
SHOW max_connections;
SELECT count(*) AS in_use,
       current_setting('max_connections')::int AS ceiling,
       current_setting('superuser_reserved_connections')::int AS reserved
FROM pg_stat_activity;

-- Per-application attribution to confirm no replica exceeds its share
SELECT application_name, usename, state, count(*)
FROM pg_stat_activity
GROUP BY application_name, usename, state
ORDER BY 4 DESC;

For MySQL instances, use SHOW VARIABLES LIKE 'max_connections'; and SHOW STATUS LIKE 'Threads_connected';. On the proxy, watch its structured logs for the absence of max connections reached warnings under normal load, and trip it deliberately under a load test to confirm the valve refuses at the configured number. In Cloud Monitoring, assert that database/postgresql/num_backends stays below 85% of the ceiling at peak. The fix is verified when: instance in_use sits comfortably under the ceiling at peak, no replica exceeds its --max-connections share, and the 53300 errors stop.

Which limit refuses the connection A connection can be refused by the application pool's own ceiling, by the Auth Proxy's max-connections flag, or by the database's max_connections, and each produces a distinguishable error. application pool ceiling acquisition timeout error names your pool library proxy max-connections connection refused at the socket proxy logs name the limit explicitly database max_connections FATAL: sorry, too many clients already affects every client, not just yours Set the limits so failures happen leftmost Your own pool should refuse first, then the proxy, and the database should never be the component that says no
Three components can refuse a connection, and the one that does determines both the error you see and the blast radius. Arrange the limits so the leftmost fires first.

Frequently Asked Questions

Does the Cloud SQL Auth Proxy pool connections?
No. The Auth Proxy is a transport security layer — it establishes an mTLS tunnel and resolves IAM identity, then forwards each client connection to the instance one-for-one. It performs no multiplexing. Every connection your client pool opens becomes one real backend process on the instance. To actually share backends, run PgBouncer behind the proxy.
What is the difference between --max-connections on the proxy and max_connections on the instance?
max_connections (the database flag) is the absolute ceiling on backend processes the instance will accept, derived from the machine tier’s memory. The proxy’s --max-connections is a local circuit breaker capping how many connections that one proxy will forward. The instance flag is the real limit; the proxy flag is a guardrail to stop a single proxy from consuming more than its share.
I raised the proxy --max-connections but still get “too many connections” — why?
Because the instance ceiling, not the proxy, is full. Raising the proxy valve lets more connections through to an already-saturated instance, which then rejects them with 53300. Check SELECT count(*) FROM pg_stat_activity against SHOW max_connections; if it is at the ceiling, reduce your client pool fan-in or raise the tier instead.
Should I raise max_connections or upgrade the machine tier?
Prefer upgrading the tier. Each backend reserves memory (scaled by work_mem), so raising max_connections beyond what the tier’s RAM supports invites OOM and per-query memory pressure. A larger tier raises the default ceiling and gives every backend more memory. Raise the flag manually only for small, deliberate increases on instances with proven headroom.
Where do I set --max-connections for a sidecar versus a shared proxy?
For a sidecar (one proxy per replica), set it to that replica’s client pool_size plus a small margin for health checks. For a shared gateway proxy serving multiple apps, set it to the aggregate connection budget those apps are collectively allowed, ensuring the sum across all proxies stays under 85% of the instance max_connections.