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.

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.

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.

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.