# Connection pooling on Cloud Run: sizing the pool, Cloud SQL's 25 connections, PgBouncer or Managed Connection Pooling

> Each Cloud Run instance opens its own pool, and the smallest Cloud SQL machine takes 25 connections. How to size pools per framework, and when to use Managed Connection Pooling or PgBouncer.

Published 2026-10-12 · Updated 2026-10-12 · [By Exodus team](https://exodus.elim.io/about) · Data, Performance
<https://exodus.elim.io/blog/connection-pooling-on-cloud-run>

Connection pooling on Cloud Run comes down to one sum: maximum instances times each instance's pool, plus jobs and a console, must fit under the database's max_connections: 25 on the smallest Cloud SQL machine, 22 of them for your app. Start with a pool of about 5, cap the instances in the same change, and reach for a bigger machine or a pooler only when that does not fit.

## In short

- A Cloud SQL micro accepts 25 connections; 3 are reserved for superusers, so your app gets 22.
- Google's samples use a pool of 5; SQLAlchemy defaults to 15 per engine and node-postgres to 10 with no wait limit.
- On a micro, with 4 connections kept for a migration and a console, a pool of 5 plus 2 fits 2 instances.
- A 1 vCPU, 3.75 GB machine accepts 100 connections for $49.31 a month, against $7.67 for the micro.
- Managed Connection Pooling needs Enterprise Plus, from $184.69 a month; PgBouncer on an e2-micro adds $6.73.

Prices are list prices in europe-west1 of October 12, 2026, before storage. Pages were read and commands run that day.

## How the numbers multiply

Postgres starts one process for every connection, and a pool is a set of open connections your app keeps and lends to requests. Every Cloud Run instance is a separate process with its own pool, so the connections your database sees add up (how to connect at all is in [connecting Cloud Run to Cloud SQL](https://exodus.elim.io/blog/connecting-cloud-run-to-cloud-sql)):

```
connections =
    max instances x (pool + overflow)
  + migration jobs and other jobs
  + you, in psql or a console
```

![A diagram in two lanes. Above, three Cloud Run instances each with a pool of 5 connect straight to Postgres, which holds 15 connections of the 22 a micro allows. Below, the same three instances connect to a pooler in transaction mode, which lends a connection for one transaction and keeps a fixed number open to Postgres, such as 10, however many instances there are](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-35bb5b2eff7e87949c2b96a5662f0e843338eab0ca17eb986ba2813e11da771c-680x424-svg/bytes "Each Cloud Run instance keeps its own pool, so the database counts all of them. A pooler in between holds a fixed number of server connections and lends them per transaction")

The other side of the sum is small. Cloud SQL for PostgreSQL sets `max_connections` from the machine's memory: 25 on the shared-core micro of about 0.5 GB, 50 on the small of about 1.7 GB, 100 from 3.75 GB, 400 from 7.5 GB, 500 from 15 GB. Postgres keeps 3 of those slots for superusers by default, and Cloud SQL gives no user the superuser attribute, so on a micro your app's users get 22. Cloud Run adds a limit of its own: through its built-in Cloud SQL connection, each instance can open at most 100 connections, and [the connecting guide](https://exodus.elim.io/blog/connecting-cloud-run-to-cloud-sql) says which paths that covers.

[Concurrency on Cloud Run](https://exodus.elim.io/blog/concurrency-on-cloud-run) has the chart of this sum against the default of 100 instances, and the note that during a deploy or a spike the instance count can briefly pass the maximum. So keep headroom: on a micro, leave room for one migration job and one person in `psql`. A database per tenant multiplies the sum again, one pool per database, which is why [a SaaS architecture on Cloud Run](https://exodus.elim.io/blog/a-saas-architecture-on-cloud-run) keeps all tenants in one.

## Step 1: count what you have

Three numbers decide everything: the database's limit, how many instances the service may run, and the pool size in your code. The first two are a read away:

```shell
gcloud sql instances list \
    --format='table(databaseVersion,settings.tier,settings.edition)'
gcloud sql instances describe app-db --format='value(settings.databaseFlags)'
gcloud run services describe app --region europe-west1 --format='value(
    spec.template.metadata.annotations."autoscaling.knative.dev/maxScale",
    spec.template.spec.containerConcurrency)'
```

An empty answer from the second command means no flags are set, so `max_connections` is the machine's default. To see the value itself and what is connected right now, ask the database:

```
show max_connections;
select usename, state, count(*)
  from pg_stat_activity
  group by 1, 2 order by 3 desc;
```

For history, Cloud Monitoring keeps the Cloud SQL metric `database/postgresql/num_backends`, the number of connections per database, and `num_backends_by_state`, which splits them into active and idle. Metrics Explorer in the console charts it.

![A terminal on our own project: the Cloud SQL instance is POSTGRES_17 on db-f1-micro in the Enterprise edition, it has no database flags, so max_connections stays at 25; the service attached to it has max instances 1 and concurrency 80; Cloud Monitoring shows at most 7 connections an hour over 7 days](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-665a08f3d79f02383174ce2ad63d4256ae204a6a9e596ed580a07379ce54d6b0-680x312-svg/bytes "Read-only gcloud on our own project, October 12, 2026. Names are masked")

Exodus team: on our own project, the one service that uses Cloud SQL runs at most 1 instance against a micro, and the busiest hour of the last week saw 7 connections. The cap came first, so the pool cannot multiply past the limit.

## Step 2: size the pool inside the app

Each library has its own name for the same two numbers: how many connections the pool keeps, and how many more it may open in a burst. Their defaults are not small, which is the usual way a service exceeds a micro without anyone choosing to:

- **node-postgres** (`pg.Pool`): `max` is 10 by default. `connectionTimeoutMillis` is 0, which means a request waits for a free connection forever.
- **Prisma 7** uses the pool of the driver adapter, which for Postgres is node-postgres: 10 connections and no wait timeout by default. **Prisma 6** set `connection_limit` to twice the physical CPU count plus one, and waited 10 seconds.
- **SQLAlchemy**: `pool_size` 5 plus `max_overflow` 10, so up to 15 connections per engine, and per process. Google's sample sets 5 and 2.
- **Django** closes its connection at the end of every request by default (`CONN_MAX_AGE = 0`), so each request opens a new one. The `"pool"` option in `OPTIONS`, in Django 6.1's documentation, gives a real pool with psycopg 3 and `psycopg[pool]`; with it, `CONN_MAX_AGE` must stay 0. Under ASGI, Django says to turn persistent connections off and use the pool.
- **Rails**: `pool` in `database.yml`, 5 in the guide's example, per process. The guide warns that threads contend for the pool when it is smaller than the threads that query at once, so with Puma give each process as many connections as threads.
- **pgx** (Go, `pgxpool`): `pool_max_conns` is the greater of 4 and the CPU count. Google's Go sample for `database/sql` sets `SetMaxOpenConns(7)`.

Then do the sum backwards from the limit. On a micro: 25 minus 3 for superusers leaves 22; keep 4 for a migration job and a console session, and 18 remain. With Google's sample of 5 plus 2, that is 2 instances. With SQLAlchemy's defaults of 15, it is one, and the second instance already fails. So set the pool and the cap together, in the same commit:

```shell
gcloud run services update app --region europe-west1 --max-instances=2
```

The same cap belongs on [the cutover day checklist](https://exodus.elim.io/blog/the-cutover-day-checklist), before real traffic arrives.

How big should one instance's pool be? As big as the number of queries it really runs at the same moment, not the requests it serves. A threaded server needs about one connection per thread that queries. An async server with concurrency 80 does not need 80: requests wait briefly for the pool instead.

To check the arithmetic before production does, we ran Postgres 17 at `max_connections` 25 with a role that is not a superuser, as on Cloud SQL, and let `pgbench` stand in for instances opening their pools. Twenty connections passed; thirty were refused with the exact error you would see on a micro:

![A terminal with the local test. Postgres 17.11 at max_connections 25 with 3 slots reserved for superusers. Four instances with pools of 5, 20 connections, run. Six instances, 30 connections, fail with FATAL remaining connection slots are reserved for roles with the SUPERUSER attribute. Through PgBouncer 1.26.0 in transaction mode with a pool of 10, 100 client connections hold 10 server connections and no transaction fails. With max_prepared_statements 0 a prepared statement fails with P_0 already exists; with 200 it runs. With a pool of one connection, a SET statement_timeout from one client is seen by the next, whose query is cancelled](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-05de6d6a5935723157a5a2361dabfeacb5ca57fd5d1995a564f80a7584be7caa-680x452-svg/bytes "Real output of our local test, October 12, 2026. The script is in our research notes and needs only Docker")

## Step 3: the pool settings that matter on Cloud Run

Cloud Run treats instances differently from a long-lived server in four ways, and each has a setting.

**Connections get dropped.** Google's Cloud Run page says connections "may be dropped, either by the database server itself, or by the platform infrastructure", and recommends a pool that reconnects broken connections. So check a connection before lending it: `pool_pre_ping=True` in SQLAlchemy, `CONN_HEALTH_CHECKS = True` in Django, and pgx pings a connection idle for more than a second by default. Then give connections a maximum age, so none lives long enough to meet a stale network path: Google's samples recycle after 30 minutes, which is `pool_recycle=1800` in SQLAlchemy, `maxLifetimeSeconds: 1800` in node-postgres and `SetConnMaxLifetime(30 * time.Minute)` in Go. pgx closes connections after an hour by default.

**CPU can stop between requests.** With request-based billing, the default, Cloud Run gives an instance CPU only while it processes requests. A pool's idle timers and background health checks run on that CPU, so between requests they wait, and an idle connection is closed late or found broken on the next checkout. The checkout check above covers that. Do not count on the pool to clean up on a timer.

**Requests should not wait forever.** If every connection is busy, a request waits for one. node-postgres and Prisma 7 default to no limit, so a burst turns into requests that hang until Cloud Run's request timeout. Set a short wait, such as `connectionTimeoutMillis: 5000`, so the error says what happened. Rails raises `ActiveRecord::ConnectionTimeoutError` after 5 seconds; SQLAlchemy waits 30.

**Instances get shut down.** An idle instance can be stopped at any time and is kept idle no longer than 15 minutes, unless it is one of the minimum instances. Before stopping it, Cloud Run sends `SIGTERM` and waits 10 seconds. Close the pool in that handler, `await pool.end()` in node-postgres, `engine.dispose()` in SQLAlchemy, so the connections end cleanly instead of lingering on the database side.

And always return connections: Google's page calls unreturned ones "leaked", and enough of them exhaust the pool. Use a `with` block in Python, `try`/`finally` around `client.release()` in node-postgres.

## When the arithmetic does not fit

When 2 instances are too few, or several services share one database, there are three ways out, in order of effort.

**A bigger machine.** The limit grows with memory. A dedicated-core Enterprise machine with 1 vCPU and 3.75 GB accepts 100 connections and costs $49.31 a month in europe-west1 before storage, against $7.67 for the micro (list prices of October 12, 2026). That is often the cheapest fix, and it changes nothing in the code. [The smallest Cloud SQL instances](https://exodus.elim.io/blog/cloud-sql-smallest-instances-and-what-they-cost) has the whole ladder.

**Raise the flag.** `gcloud sql instances patch --database-flags` can set `max_connections`; it replaces every flag already set, so list them all. Each connection is a process with its own memory, so on a micro more connections leave less memory for queries.

**A pooler.** A pooler holds a fixed set of server connections to Postgres and lends one to a client for as long as a transaction lasts. That is transaction mode, and it is what makes 100 clients fit into 10 connections. In our test, 100 pgbench clients through PgBouncer with a pool of 10 held exactly 10 server connections and finished with no failed transaction.

![A bar chart of connections at Postgres in our local test. Straight to Postgres, 20 clients hold 20 connections and 30 clients are refused, above the dashed line at 22, which is max_connections 25 minus 3 for superusers. Through PgBouncer with a pool of 10, 20 clients and 100 clients each hold 10 connections](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-c75cc0f8306d7b681c05a9c7fee1c8a1121376d55b73d366a149e133eeae7b05-680x360-svg/bytes "Our local test, October 12, 2026. Without a pooler the connections equal the clients; with one in transaction mode they equal the pool")

## What transaction mode changes

Between two transactions, a client may get a different server connection. Anything your code leaves in the session, rather than in a transaction, can be lost or leak to another client. PgBouncer's feature table and Google's limits list for Managed Connection Pooling name the same things that do not work in transaction mode:

- `SET` and `RESET` of session settings, such as `search_path`, `statement_timeout` or a time zone set by the app
- `LISTEN`
- cursors `WITH HOLD`
- SQL-level `PREPARE` and `DEALLOCATE`
- temporary tables with `ON COMMIT PRESERVE ROWS` or `DELETE ROWS`
- `LOAD`
- session-level advisory locks, which some migration tools take

In our test with a pool of one connection, one client ran `SET statement_timeout = '50ms'` and disconnected. The next client saw 50 ms and had its query cancelled. Use `SET LOCAL` inside a transaction, or set the value on the role with `ALTER ROLE app SET ...`.

**Prepared statements** are the other trap. Many drivers prepare statements at the protocol level, Prisma among them. PgBouncer supports that in transaction mode when `max_prepared_statements` is above 0, and its default is 200. Managed Connection Pooling's default is 0. With 0, our test failed at once with `prepared statement "P_0" already exists`; with 200 it ran. Prisma's page adds that `pgbouncer=true` in the URL is only for PgBouncer older than 1.21.0, and that Prisma Migrate needs a direct connection, so give the CLI a second URL that bypasses the pooler. Django needs `DISABLE_SERVER_SIDE_CURSORS = True` for a connection that goes through a pooler in transaction mode.

Run migrations around the pooler: a [migration job](https://exodus.elim.io/blog/database-migrations-as-cloud-run-jobs) connects straight to the database and often takes a session lock.

## Managed Connection Pooling

Cloud SQL's own pooler runs next to the database, so nothing new runs in your project. Its requirements are strict:

- The instance is in the **Enterprise Plus** edition. The smallest Enterprise Plus machine, `db-perf-optimized-N-2` with 2 vCPUs and 16 GB, costs $184.69 a month in europe-west1 before storage, at the list prices of October 12, 2026, about 24 times a micro. It also comes with a default `max_connections` of 500, which may be enough without a pooler.
- The client connects directly or through the Cloud SQL Auth Proxy 2.15.2 or later. Port 6432 is the pooler for direct connections, 3307 for the Auth Proxy; 5432 still goes straight to Postgres.
- The instance uses the new network architecture and a recent maintenance version, and enabling the pooler on an existing instance restarts the database.

```shell
gcloud sql instances patch app-db --enable-connection-pooling
gcloud sql instances patch app-db \
  --connection-pool-flags=max_pool_size=20,max_prepared_statements=200
```

The second command sets the pooler's own flags. The defaults are transaction mode, `max_pool_size` 50 per database and user pair, 5,000 client connections, a 120-second wait for a free connection, and `max_prepared_statements` 0. Every value applies per pooler, and there is one pooler for every 4 vCPUs, at least one. Google asks for at least 15 server connections per CPU reserved for the pooler in `max_connections`.

One gap in Google's pages: the requirements name direct connections and the Auth Proxy, and Cloud Run's page links to the pooler, but no page we read says whether Cloud Run's built-in `/cloudsql` socket reaches it. The documented paths from Cloud Run are a direct connection to port 6432 over [private IP](https://exodus.elim.io/blog/connecting-cloud-run-to-cloud-sql), or the Auth Proxy as a sidecar container. Test the built-in socket on a staging instance before you depend on it.

## Your own PgBouncer

PgBouncer is an open-source pooler for Postgres that uses about 2 kB of memory per connection. The question on Google Cloud is where to run it:

- **Not as a Cloud Run service.** A service receives HTTP and gRPC requests, not raw Postgres traffic, and services have no Direct VPC ingress.
- **Not as a sidecar** in each instance: every instance still has its own, so the total does not fall.
- **A worker pool** with Direct VPC ingress can accept TCP connections on a private IP. An instance learns its address when it starts, so Google's tutorial registers each one in Cloud DNS from a startup script. It works, and it is the most moving parts.
- **A small Compute Engine VM** with a fixed internal IP is the plain choice. An `e2-micro`, 0.25 vCPU and 1 GB, costs $6.73 a month in europe-west1 before its disk, at the list prices of October 12, 2026. Create it with `--no-address` and a `--private-network-ip` in your subnet, and reach it from Cloud Run through Direct VPC egress, as [moving a Compute Engine VM to Cloud Run](https://exodus.elim.io/blog/compute-engine-vm-to-cloud-run) shows.

The core of the configuration is short. PgBouncer's own default mode is `session`, which does not reduce connections, so set transaction mode explicitly:

```
[databases]
app = host=DB_PRIVATE_IP port=5432 dbname=app

[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
default_pool_size = 10
max_client_conn = 500
max_prepared_statements = 200
```

The VM is then yours: a single point of failure that needs patching, with credentials in its `auth_file`, which should come from Secret Manager at boot. Admit only your Cloud Run subnet in the firewall. Weigh that upkeep against $41.64 a month, the difference between the micro and the 100-connection Enterprise machine.

## What it costs

The in-app pool costs nothing. A month in europe-west1, at the list prices of October 12, 2026, before storage and backups, with 730 hours: the micro with 25 connections is $7.67; the 1 vCPU, 3.75 GB Enterprise machine with 100 is $49.31; Enterprise Plus, which Managed Connection Pooling needs and which has no price line of its own on the pages we read, starts at $184.69; PgBouncer on an e2-micro adds $6.73 plus its disk and your time.

## What breaks

- **"FATAL: remaining connection slots are reserved for roles with the SUPERUSER attribute".** Instances times pool passed `max_connections`. Lower the pool, cap `--max-instances`, or raise the limit.
- **Requests hang, then time out.** The pool is exhausted and has no wait limit, or connections leak. Set the wait timeout and check that every code path returns its connection.
- **"could not obtain a database connection within 5.000 seconds"** from Rails, `ActiveRecord::ConnectionTimeoutError`. Rails' pool is smaller than Puma's threads.
- **Errors on the first query after a quiet hour.** The pool lent a connection that was dropped. Turn on the checkout check and a maximum lifetime.
- **Prepared statement "P_0" already exists**, or "s0" from Prisma Migrate. A pooler in transaction mode without prepared statement support, or migrations sent through the pooler.
- **A setting or a lock that seems to belong to someone else.** A session `SET` or advisory lock under transaction pooling.
- **"query_wait_timeout".** A client waited for a pooled connection longer than the pooler allows, 120 seconds by default on Managed Connection Pooling. The pool is too small for the work, or a long transaction holds connections.
- **Connections jump during a deploy or a spike.** The instance count can briefly pass the maximum, as the concurrency guide explains. Keep the headroom from Step 1.

## Checklist

1. Read `max_connections`, max instances and the pool size of every service and job on the database.
1. Subtract 3 for superusers and a few for migrations and a console; set `--max-instances` and the pool to fit.
1. Start with a pool of about 5 per instance and change it only after measuring `num_backends`.
1. Turn on a checkout check, a maximum lifetime of about 30 minutes and a pool wait timeout.
1. Close the pool on `SIGTERM`.
1. If you need more, price a bigger machine before a pooler.
1. With a pooler: transaction mode, prepared statements on, no session `SET`, migrations on a direct connection.

## Where Exodus comes in

When Exodus audits a repository, it reads the database driver and its pool settings, the web server's threads, and the service's concurrency. The report lists the pool size and `--max-instances` that fit the Cloud SQL machine in the plan, flags defaults that would exceed it, such as SQLAlchemy's 15 or Django's connection per request, and says whether the app uses session features that a pooler in transaction mode would break.

## FAQ

### How many connections does the smallest Cloud SQL instance allow?

Cloud SQL for PostgreSQL sets max_connections from memory: 25 on the shared-core micro of about 0.5 GB, 50 on the small, 100 from 3.75 GB and 400 from 7.5 GB. Postgres keeps 3 slots for superusers, and Cloud SQL gives no user that attribute, so on a micro your app's users get 22.

### What pool size should a Cloud Run service use?

About as many connections as queries an instance really runs at the same moment, not the requests it serves. Google's samples use 5, with 2 more in a burst. A threaded server needs about one per thread that queries; an async server at concurrency 80 does not need 80, because requests wait briefly for the pool.

### Why does Cloud Run fail with "remaining connection slots are reserved"?

Instances times pool size passed the database's max_connections. Each Cloud Run instance opens its own pool, and during a deploy or a spike the instance count can briefly pass the maximum. Lower the pool, cap the maximum instances, or move to a machine with a higher limit.

### Do I need PgBouncer on Cloud Run?

Usually not at first. A bigger machine is often the cheaper fix: 100 connections for $49.31 a month. PgBouncer cannot run as a Cloud Run service, which takes only HTTP and gRPC, so it goes on a small VM, an e2-micro at $6.73 a month, which you then patch and secure yourself.

## Sources

1. [Managed Connection Pooling overview, Cloud SQL for PostgreSQL, Google (Enterprise Plus edition only; direct connection or the Cloud SQL Auth Proxy; Auth Proxy 2.15.2 or later; one pooler per 4 vCPUs, at least one; transaction mode by default, session mode optional; max_pool_size 50 per database and user pair per pooler, max_client_connections 5,000 per pooler, max_prepared_statements 0, query_wait_timeout 120 s, server_connection_idle_timeout 600 s, server_lifetime 3,600 s by default; SET/RESET, LISTEN, WITH HOLD cursors, PREPARE/DEALLOCATE, PRESERVE/DELETE ROWS temp tables, LOAD and session-level advisory locks unsupported in transaction mode; enabling it on an existing instance restarts the database; ports 5432, 6432 and 3307; reserve at least 15 server connections per CPU in max_connections; page updated Oct 9, 2026, read Oct 12, 2026)](https://docs.cloud.google.com/sql/docs/postgres/managed-connection-pooling)
2. [Configure Managed Connection Pooling, Cloud SQL for PostgreSQL, Google (gcloud 515.0.0 or later; gcloud sql instances patch --enable-connection-pooling and --connection-pool-flags; --no-enable-connection-pooling; disabling restarts the instance; Terraform example on db-perf-optimized-N-2, ENTERPRISE_PLUS; read Oct 12, 2026)](https://docs.cloud.google.com/sql/docs/postgres/configure-mcp)
3. [Connect from Cloud Run, Cloud SQL for PostgreSQL, Google (connections may be dropped by the database server or the platform, use a pool that reconnects; Managed Connection Pooling pointer; read Oct 12, 2026)](https://docs.cloud.google.com/sql/docs/postgres/connect-run)
4. [Manage database connections, Cloud SQL for PostgreSQL, Google (a pool is a cache of connections borrowed and returned; leaked connections are not reused; samples: SQLAlchemy pool_size 5 and max_overflow 2, pool_timeout 30 s, pool_recycle 1,800 s; HikariCP maximumPoolSize 5, connectionTimeout 10 s, maxLifetime 30 minutes; Knex pool max 5; Go SetMaxOpenConns 7 and SetConnMaxLifetime 1,800 s; Rails pool 5; exponential backoff on connect; retry a broken transaction from its start; read Oct 12, 2026)](https://docs.cloud.google.com/sql/docs/postgres/manage-connections)
5. [Quotas and limits, Cloud SQL for PostgreSQL, Google (max_connections set by the machine; SELECT * FROM pg_settings WHERE name = 'max_connections'; with the built-in Cloud SQL connection each Cloud Run instance is limited to 100 connections, not with a sidecar Auth Proxy, the connectors or a direct IP connection; read Oct 12, 2026)](https://docs.cloud.google.com/sql/docs/postgres/quotas)
6. [Configure database flags, Cloud SQL for PostgreSQL, Google (max_connections default by memory: tiny about 0.5 GB 25, small about 1.7 GB 50, 3.75 to 6 GB 100, 6 to 7.5 GB 200, 7.5 to 15 GB 400, 15 to 30 GB 500; minimum 14; read Oct 12, 2026)](https://docs.cloud.google.com/sql/docs/postgres/flags)
7. [About PostgreSQL users and roles, Cloud SQL, Google (customers cannot create or use users with superuser attributes; cloudsqlsuperuser instead; read Oct 12, 2026)](https://docs.cloud.google.com/sql/docs/postgres/users)
8. [Machine series overview, Cloud SQL for PostgreSQL, Google (db-perf-optimized-N-2: 2 vCPUs, 16 GB, the smallest N2 machine of Enterprise Plus; read Oct 12, 2026)](https://docs.cloud.google.com/sql/docs/postgres/machine-series-overview)
9. [Cloud Billing Catalog API, Cloud SQL and Compute Engine SKUs in europe-west1 (PostgreSQL zonal: micro $0.0105 an hour, Enterprise vCPU $0.0413 and RAM $0.007 per GiB-hour, Enterprise Plus N vCPU $0.0537 and RAM $0.0091 per GiB-hour; E2 core $0.02399337 and RAM $0.00321609 per GiB-hour; 730 hours a month; list prices of Oct 12, 2026)](https://cloud.google.com/sql/pricing)
10. [General-purpose machine family, Compute Engine, Google (e2-micro: 0.25 vCPU, two vCPUs at 12.5% each, 1 GB; read Oct 12, 2026)](https://docs.cloud.google.com/compute/docs/general-purpose-machines)
11. [Billing settings for services, Cloud Run, Google (request-based billing allocates CPU only during request processing; idle instances, including minimum instances, can be shut down at any time; SIGTERM gives 10 seconds; read Oct 12, 2026)](https://docs.cloud.google.com/run/docs/configuring/billing-settings)
12. [Container runtime contract, Cloud Run, Google (an idle instance is kept no longer than 15 minutes unless minimum instances; SIGTERM, then SIGKILL after 10 seconds; services and jobs support Direct VPC egress, not ingress; worker pools with Direct VPC ingress listen for TCP such as database connections; requests reach the container as HTTP/1 or gRPC; read Oct 12, 2026)](https://docs.cloud.google.com/run/docs/container-contract)
13. [Direct VPC with a VPC network, Cloud Run, Google (services and jobs do not support Direct VPC ingress; worker pools support egress and ingress; read Oct 12, 2026)](https://docs.cloud.google.com/run/docs/configuring/vpc-direct-vpc)
14. [Register private IP addresses for worker pools using Cloud DNS, Cloud Run, Google (a startup script reads the instance's IP from the metadata server and registers it in a Cloud DNS zone; read Oct 12, 2026)](https://docs.cloud.google.com/run/docs/tutorials/worker-pool-private-ip-dns)
15. [Cloud SQL metrics, Google (database/postgresql/num_backends, PostgreSQL connections, per database; num_backends_by_state; read Oct 12, 2026)](https://docs.cloud.google.com/sql/docs/postgres/admin-api/metrics)
16. [PgBouncer features (session, transaction and statement pooling; 2 kB per connection; the SQL feature map: SET/RESET, LISTEN, WITH HOLD cursors, PREPARE/DEALLOCATE, PRESERVE/DELETE ROWS temp tables and LOAD never work in transaction pooling, protocol-level prepared plans do; PgBouncer 1.26.0 in September 2026; read Oct 12, 2026)](https://www.pgbouncer.org/features.html)
17. [PgBouncer configuration (pool_mode session by default; listen_port 6432; max_client_conn 100; default_pool_size 20; max_prepared_statements 200, 0 disables prepared statement support in transaction mode; auth_file; read Oct 12, 2026)](https://www.pgbouncer.org/config.html)
18. [Connections and authentication, PostgreSQL 18 documentation (superuser_reserved_connections defaults to three; reserved_connections defaults to zero; read Oct 12, 2026)](https://www.postgresql.org/docs/current/runtime-config-connection.html)
19. [node-postgres, new Pool (max 10 by default; idleTimeoutMillis 10,000; connectionTimeoutMillis 0, no timeout; maxLifetimeSeconds 0, disabled; read Oct 12, 2026)](https://node-postgres.com/apis/pool)
20. [Prisma ORM 7, Connection pool (v7 uses the driver adapter's pool: pg adapter max 10, connectionTimeoutMillis 0, idleTimeoutMillis 10 s; v6 connection_limit num_cpus::get_physical() * 2 + 1, pool_timeout 10 s; read Oct 12, 2026)](https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/databases-connections/connection-pool)
21. [Prisma ORM 7, Configure Prisma Client with PgBouncer (transaction mode required; pgbouncer=true only below PgBouncer 1.21.0; max_prepared_statements above 0; Prisma Migrate needs a direct connection, else 'prepared statement "s0" already exists'; read Oct 12, 2026)](https://www.prisma.io/docs/orm/v7/prisma-client/setup-and-configuration/databases-connections/pgbouncer)
22. [SQLAlchemy 2.0, Connection Pooling (QueuePool pool_size 5, max_overflow 10, timeout 30 s by default; pool_pre_ping; pool_recycle; NullPool; read Oct 12, 2026)](https://docs.sqlalchemy.org/en/20/core/pooling.html)
23. [Django 6.1, Databases (CONN_MAX_AGE 0 by default closes the connection at the end of each request; persistent connections off under ASGI; the psycopg pool option; CONN_HEALTH_CHECKS; DISABLE_SERVER_SIDE_CURSORS for transaction pooling; read Oct 12, 2026)](https://docs.djangoproject.com/en/6.1/ref/databases)
24. [Rails Guides, Configuring Rails Applications, Database Pooling (pool in database.yml, 5 in the example; ActiveRecord::ConnectionTimeoutError 'could not obtain a database connection within 5.000 seconds'; read Oct 12, 2026)](https://guides.rubyonrails.org/configuring.html)
25. [pgxpool, pgx v5 (pool_max_conns default the greater of 4 or runtime.NumCPU(); pool_max_conn_lifetime 1 hour; pool_max_conn_idle_time 30 minutes; pool_health_check_period 1 minute; ping after 1 second idle; read Oct 12, 2026)](https://pkg.go.dev/github.com/jackc/pgx/v5/pgxpool)
26. [gcloud reference, Google Cloud SDK 587.0.0 (sql instances patch --[no-]enable-connection-pooling, --connection-pool-flags, --clear-connection-pool-flags, --database-flags; compute instances create --machine-type, --subnet, --no-address, --private-network-ip; --help read Oct 12, 2026)](https://docs.cloud.google.com/sdk/gcloud/reference/sql/instances/patch)
27. Exodus team: a local test with Postgres 17.11 at max_connections 25 and PgBouncer 1.26.0 in transaction mode (pgbench: 20 direct connections pass, 30 are refused; 20 and 100 clients through a pool of 10 hold 10 server connections with no failed transaction; max_prepared_statements 0 fails prepared statements, 200 passes; a SET leaks to the next client), and read-only gcloud and Cloud Monitoring on our own project (one db-f1-micro, Enterprise edition, no flags; one service with max instances 1 and concurrency 80; peak num_backends 7 over 7 days), Oct 12, 2026
