What this usually means
The Cloud SQL instance has hit its configured max_connections limit. For shared-core tiers (db-f1-micro, db-g1-small) this is 250 or 500. For dedicated tiers, it's typically 4000. The limit exists to protect the instance from resource exhaustion, but common causes include connection pool misconfiguration (too many idle connections, pool size too large), leaked connections from not closing them properly, or a traffic spike that wasn't anticipated. Unlike a normal database server, Cloud SQL has built-in connection management that can be tuned via flags, but many teams overlook this until production breaks.
The first ten minutes — establish facts before touching code.
- 1Check Cloud Monitoring metrics: 'cloudsql.googleapis.com/database/postgresql/num_backends' (or MySQL equivalent) — look for the count hitting the limit.
- 2Run 'SHOW VARIABLES LIKE "max_connections";' on the database to confirm the configured limit.
- 3Query current connections: 'SELECT COUNT(*) FROM pg_stat_activity;' (PostgreSQL) or 'SHOW PROCESSLIST;' (MySQL) to see active vs idle connections.
- 4Check application-level connection pool settings: Spring Boot's spring.datasource.hikari.maximum-pool-size, Rails' pool, Node's knex pool min/max.
- 5Look at recent Cloud SQL logs for authentication failures or connection rejections (gcloud logging read 'resource.type=cloudsql_database' --limit=50).
- 6Verify if the instance is under-provisioned: db-f1-micro max_connections=25 (MySQL) or 250 (PostgreSQL) — check the tier limits.
The specific files, logs, configs, and dashboards that usually own this bug.
- searchCloud Console > Cloud SQL > Instance > Metrics tab: connections, cpu, memory
- searchCloud Logging: filter resource.type=cloudsql_database AND ("too many connections" OR "connection limit")
- searchDatabase system tables: pg_stat_activity (PostgreSQL) or information_schema.processlist (MySQL)
- searchApplication config files: application.properties, database.yml, knexfile.js
- searchCloud SQL flags page: max_connections, idle_in_transaction_session_timeout
- searchVPC firewall rules: if connections are coming from unexpected IPs
Practical causes, not theory. These are the things you will actually find.
- warningConnection pool max size too large for the instance tier (e.g., HikariCP pool=100 on a db-f1-micro with max_connections=25)
- warningIdle connections not released: no timeout on idle sessions, or connection leak in code (e.g., missing close() in try-finally)
- warningTraffic spike from a new feature or marketing campaign overwhelming the pool
- warningMultiple app instances each with independent pools summing to exceed max_connections
- warningLong-running queries holding connections open (e.g., unoptimized report queries)
- warningMisconfigured connection limits: max_connections flag set too low or not tuned after scaling
Concrete fix directions. Pick the one that matches your root cause.
- buildReduce connection pool size per app instance: match total instances * pool size to 80% of max_connections (e.g., 10 instances x 20 pool = 200, leave 50 for admin)
- buildSet idle connection timeout: PostgreSQL idle_in_transaction_session_timeout=30000 (30s), MySQL wait_timeout=600
- buildIncrease max_connections via Cloud SQL flag: go to Console > Instance > Edit configuration > Flags > add max_connections (up to 4000 for dedicated tiers)
- buildScale up instance tier: e.g., from db-f1-micro (25 connections) to db-g1-small (250)
- buildAdd connection pooling middleware: PgBouncer or ProxySQL to multiplex app connections
- buildImplement retry with exponential backoff in the application to avoid thundering herd on reconnect
A fix you cannot prove is a guess. Close the loop.
- verifiedMonitor the num_backends metric dropping below the limit after the fix
- verifiedRun a load test with expected traffic: ensure connections never exceed 80% of max_connections
- verifiedCheck application logs for absence of 'too many connections' errors over a full business cycle
- verifiedVerify idle connections are cleaned: query pg_stat_activity and confirm idle sessions timeout
- verifiedTest failover: kill a few connections and see that new connections succeed
- verifiedCompare connection graphs before/after: ensure the curve flattens at a safe level
Things that make this bug worse or harder to find.
- warningJust increasing max_connections without reducing pool sizes — this just delays the problem and can crash the database from memory pressure
- warningSetting max_connections to the absolute maximum without considering instance memory (each connection uses ~5-10MB)
- warningIgnoring connection leaks: adding more pools when the real issue is unclosed connections
- warningNot coordinating across multiple app versions: old versions may still use old pool sizes
- warningAssuming the default max_connections is safe for all tiers — always check the tier's limit
- warningRestarting the database to clear connections during an outage — drops all active queries and causes more retries
Black Friday Traffic Spike Hits Cloud SQL Connection Limit
Timeline
- 09:15PagerDuty alert: 'Too many connections' errors in production for 2 minutes
- 09:18Check Cloud SQL metrics: num_backends=495, max_connections=500
- 09:20Run 'SELECT count(*) FROM pg_stat_activity' — 495 connections, 400 idle in transaction
- 09:22Check knex pool settings: each app instance pool min=10, max=100, total 4 instances = max 400
- 09:25Discover a new background job that opens connections without closing them (leak)
- 09:30Emergency: set max_connections=1000 via Cloud SQL flag, reduce pool max to 50 per instance
- 09:35Connections drop to 200, errors stop
- 10:00Deploy fix for connection leak in background job
- 11:00Revert max_connections back to 500, monitor for 24 hours
It was Black Friday, and traffic was already 3x normal. At 09:15, our alerting fired: 'Too many connections' errors were spiking in the Node.js app logs. I jumped into Cloud Monitoring and saw num_backends at 495 — one away from our max_connections of 500. I knew we had to act fast before the database became completely unreachable.
I ran pg_stat_activity and was shocked: 400 of the 495 connections were 'idle in transaction'. That's a classic sign of connection leaks. I checked our knex.js pool config: each of our 4 app instances had a pool max of 100, which theoretically maxed at 400, but the extra 95 connections pointed to unclosed connections from a new background job we'd deployed yesterday.
I immediately increased max_connections to 1000 via Cloud SQL flag and reduced the pool max to 50 per instance (total 200). Within minutes, connections dropped to 200 and errors stopped. After the rush, we fixed the background job's connection handling and reverted max_connections to 500. The lesson: always set idle transaction timeouts and monitor connection counts proactively.
Root cause
A background job opened database connections without closing them (connection leak), combined with aggressive connection pool sizes (max=100 per instance across 4 instances) that together consumed the 500 connection limit.
The fix
Increased max_connections temporarily to 1000, reduced pool size to 50 per instance, and deployed a fix to properly close connections in the background job. Added idle_in_transaction_session_timeout=60000 to auto-kill idle sessions.
The lesson
Always set connection pool sizes to a fraction of max_connections, account for all app instances, and enforce idle timeouts. Monitor connection metrics before peak events.
GCP Cloud SQL imposes default max_connections based on the instance tier. For shared-core tiers: db-f1-micro has 25 connections (MySQL) or 250 (PostgreSQL); db-g1-small has 50 (MySQL) or 250 (PostgreSQL). For dedicated tiers: 4000 connections, but the actual limit can be increased up to 4000 via the max_connections flag. However, increasing max_connections beyond the default consumes memory — each connection uses about 5-10 MB. On a 2 vCPU, 7.5 GB instance (db-custom-2-7680), 4000 connections would use 20-40 GB of memory, causing OOM kills. The safe formula: max_connections = (total_memory * 0.7) / (5 MB). Never set max_connections arbitrarily high without checking memory.
The default max_connections for your tier is a safe starting point, but many teams hit the limit because they don't realize how many connections their application pool creates multiplied by the number of app instances. For example, a Kubernetes deployment with 10 replicas, each with HikariCP pool size 50, sums to 500 connections — which exceeds a db-f1-micro's 250 PostgreSQL limit.
The most common fix is adjusting the connection pool size per application instance. The total connections = (number of app instances) * (pool max size). Keep this at 80% of max_connections to leave room for administrative connections. For example, if max_connections=500 and you have 4 app instances, set pool max to 100 each (total 400). Also set a minimum pool size to avoid creating connections on every request (min=5 is typical).
Beyond sizing, set idle connection timeouts. In PostgreSQL, set idle_in_transaction_session_timeout to 30-60 seconds to kill connections stuck in idle transactions. In MySQL, set wait_timeout and interactive_timeout to 600 seconds. Also enforce a statement timeout to prevent long-running queries from holding connections. These settings can be configured as Cloud SQL database flags.
Set up Cloud Monitoring alerts on the metric cloudsql.googleapis.com/database/postgresql/num_backends (or MySQL equivalent). Alert when utilization exceeds 80% of max_connections for 5 minutes. This gives you time to react before hitting the hard limit. Also monitor 'connections timed out' metrics if available.
For deeper visibility, enable pg_stat_statements extension (PostgreSQL) or performance_schema (MySQL) to track which queries are using connections. Run weekly reviews of connection usage patterns, especially after deploying new features or scaling instances.
If you can't connect to the database at all, you need to kill idle connections. Use the Cloud SQL Console's 'Kill connections' button (MySQL) or run the following command via Cloud SQL Proxy: 'gcloud sql instances describe INSTANCE_NAME' to find the private IP, then connect via psql as a superuser. If superuser connections are also limited, you may need to restart the instance (last resort).
Alternatively, use the Cloud SQL API to temporarily increase max_connections. This can be done via gcloud: 'gcloud sql instances patch INSTANCE_NAME --database-flags max_connections=1000'. This takes effect within minutes without restart. After the crisis, revert the flag and fix the root cause.
Frequently asked questions
What is the default max_connections for a db-f1-micro Cloud SQL instance?
For MySQL, it's 25 connections. For PostgreSQL, it's 250 connections. Always check the documentation for your database engine, as the defaults vary significantly.
Can I increase max_connections beyond 4000 on dedicated tiers?
No. Cloud SQL enforces a hard cap of 4000 connections regardless of the flag value. If you need more, consider using a connection pooler like PgBouncer or ProxySQL to multiplex connections.
How do I find which application or user is using the most connections?
For PostgreSQL, run: SELECT usename, count(*) FROM pg_stat_activity GROUP BY usename ORDER BY count DESC; For MySQL: SELECT user, COUNT(*) FROM information_schema.processlist GROUP BY user; This shows per-user connection counts.
What happens if I set max_connections too high?
Each connection consumes memory (about 5-10 MB). If max_connections is set too high for the instance's available memory, the database can become unstable, crash, or be killed by the OS OOM killer. Always calculate based on instance memory.
Does a read replica have its own connection limit?
Yes, each read replica is a separate Cloud SQL instance with its own max_connections setting. If you are hitting the limit on a replica, you need to configure its connection pool or scale it independently.