Skip to content
Back to skills

Db Connection Sizing

ASecurity

> Tuning max_connections, understanding per-connection memory overhead, and right-sizing database connections for on-premise and serverless environments.

  • 20 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 11, 2026
businesssqlawsdebuggingapidatabasebackend

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 11, 2026

npx -y skills add Intense-Visions/harness-engineering --skill db-connection-sizing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Db Connection Sizing?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Db Connection Sizing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/intense-visions-db-connection-sizing/badge)](https://www.skillsdirectory.com/skills/intense-visions-db-connection-sizing)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# Connection Sizing

> Tuning max_connections, understanding per-connection memory overhead, and right-sizing database connections for on-premise and serverless environments.

## When to Use

- Setting `max_connections` for a new PostgreSQL deployment
- Debugging `FATAL: too many connections` errors
- Estimating memory requirements for a database server
- Deploying to serverless platforms with connection limits (Neon, Supabase, PlanetScale)
- Deciding whether to add replicas or increase connection limits

## Instructions

### Key Concepts

**1. Per-Connection Memory Cost**

Each PostgreSQL connection is a separate OS process. Memory per connection:

```
Base process overhead:     ~5-10 MB
work_mem (per sort/hash):  4 MB default (can be higher)
temp_buffers:              8 MB default
maintenance_work_mem:      64 MB (only during VACUUM, CREATE INDEX)

Worst case per connection: 10 MB base + work_mem * active_operations
```

A server with 16 GB RAM cannot safely support 1,000 connections. At 10 MB per connection, that is 10 GB just for connection overhead, leaving little for `shared_buffers` and OS cache.

**2. The max_connections Formula**

```
max_connections = (available_ram - shared_buffers - os_reserve)
                  / per_connection_memory

Example:
  Total RAM:           32 GB
  shared_buffers:       8 GB (25% of RAM)
  OS and file cache:    8 GB
  Available:           16 GB
  Per connection:      10 MB (conservative)

  max_connections = 16 GB / 10 MB = 1,600

  Practical max: ~400 (to leave headroom for work_mem spikes)
```

Conservative rule of thumb: set `max_connections` to 2-4x the number of CPU cores for OLTP workloads. A 16-core server should start with 50-100 connections.

**3. work_mem Impact**

`work_mem` is allocated per sort or hash operation, not per connection. A single complex query can allocate `work_mem` multiple times:

```sql
-- This query might allocate work_mem 3 times:
-- once for the sort, once for the hash join, once for the subquery
SELECT * FROM orders o
  JOIN customers c ON o.customer_id = c.id
  WHERE o.total > (SELECT avg(total) FROM orders)
  ORDER BY o.created_at;

-- If work_mem = 256MB and 50 connections run this simultaneously:
-- Memory spike: 50 * 256MB * 3 = 37.5 GB (likely OOM)
```

Set `work_mem` conservatively at the server level and increase it per-session for analytical queries:

```sql
-- Server-level: conservative
ALTER SYSTEM SET work_mem = '16MB';

-- Per-session for analytics:
SET work_mem = '256MB';
SELECT ... complex analytical query ...;
RESET work_mem;
```

**4. Reserved Connections**

Always reserve connections for administrative access:

```sql
-- postgresql.conf
max_connections = 100
superuser_reserved_connections = 3  -- default

-- PostgreSQL 16+ adds:
reserved_connections = 5  -- for pg_use_reserved_connections role
```

When all 100 connections are used, the 3 reserved superuser connections let DBAs connect to diagnose and fix the issue.

### Worked Example

Sizing connections for a web application on a 16-core, 64 GB server:

```
Step 1: Allocate shared_buffers
  shared_buffers = 16 GB (25% of 64 GB)

Step 2: Reserve OS memory
  OS and file cache = 16 GB

Step 3: Calculate available memory for connections
  Available = 64 - 16 - 16 = 32 GB

Step 4: Estimate per-connection memory
  Base: 10 MB
  work_mem: 16 MB (default, low)
  Conservative per-connection: 26 MB

Step 5: Calculate max_connections
  max_connections = 32 GB / 26 MB = ~1,230
  Practical limit (with headroom): 400

Step 6: Configure
  max_connections = 400
  superuser_reserved_connections = 5
  Application pool: 395 available
  Per-service pool: 395 / 8 services = ~49 per service

Step 7: Add PgBouncer for multiplexing
  PgBouncer default_pool_size = 100
  PgBouncer max_client_conn = 2000
  Each service can open up to 250 connections to PgBouncer
```

Verify under load:

```sql
-- Current connection count
SELECT count(*) FROM pg_stat_activity;

-- Memory per backend (approximate)
SELECT pg_size_pretty(sum(allocated_bytes))
FROM pg_backend_memory_contexts
WHERE pid = pg_backend_pid();
```

### Anti-Patterns

1. **Setting `max_connections = 1000` on a small instance.** Even if connections are mostly idle, each consumes base memory and contends for lightweight locks. More connections does not mean more throughput. Beyond a threshold, throughput decreases due to contention.

2. **Setting `work_mem` high globally.** `work_mem = 1GB` globally with 100 connections can cause 100+ GB of memory allocation during peak sort operations. Set it low globally and increase per-session when needed.

3. **Not accounting for connection pools in each service.** If 10 services each maintain a pool of 50 connections, the database needs 500 connections minimum. Map out all connection sources before setting `max_connections`.

4. **Ignoring monitoring connections.** Prometheus exporters, pgAgent, logical replication slots, and backup tools all consume connections. Reserve 10-20 connections beyond what application services need.

5. **Using the same sizing for OLTP and OLAP.** OLTP queries are short and need many connections with low `work_mem`. OLAP queries are long and need few connections with high `work_mem`. Size each workload separately or use separate connection pools.

### PostgreSQL Specifics

- `pg_stat_activity` shows all active connections, their state, and current query.
- `pg_backend_memory_contexts` (PostgreSQL 14+) shows per-backend memory allocation breakdown.
- `idle_in_transaction_session_timeout` kills connections that sit in an open transaction without activity.
- `statement_timeout` prevents runaway queries from holding connections indefinitely.
- `tcp_keepalives_idle`, `tcp_keepalives_interval`, `tcp_keepalives_count` detect and close dead connections.

## Details

### Advanced Topics

**Serverless Connection Constraints:**

Serverless PostgreSQL providers impose strict connection limits:

| Provider    | Free Tier   | Pro Tier | Pooler Mode         |
| ----------- | ----------- | -------- | ------------------- |
| Neon        | 100         | 500      | Built-in, tx mode   |
| Supabase    | 60          | 200      | PgBouncer, tx mode  |
| PlanetScale | N/A (MySQL) | 10K      | ProxySQL            |
| AWS RDS     | Instance    | Instance | RDS Proxy available |

For serverless, always use a connection pooler. Direct connections from serverless functions (Lambda, Vercel Functions) create and destroy connections rapidly, exhausting the pool.

**Connection Lifecycle Tuning:**

```ini
# postgresql.conf
tcp_keepalives_idle = 60      # detect dead connections after 60s
tcp_keepalives_interval = 10  # probe every 10s after idle timeout
tcp_keepalives_count = 3      # close after 3 failed probes

idle_in_transaction_session_timeout = '30s'  # kill idle-in-tx
statement_timeout = '30s'                     # kill long queries
```

**Connection Pooling Interaction:**

```
Application Pool (per service) → PgBouncer → PostgreSQL
      50 connections          →  100 slots → max_connections = 120

Application sees: 50 connections (fast, local)
PgBouncer sees: 100 database connections to manage
PostgreSQL sees: 100 backends (+ 20 reserved)
```

### Engine Differences

MySQL uses threads, not processes. Per-thread memory is significantly lower (~256KB-1MB stack plus per-thread buffers). MySQL can handle 5,000+ connections on the same hardware where PostgreSQL struggles at 500. However, MySQL's thread scheduler contention becomes the bottleneck at high connection counts. `thread_pool` plugin (Enterprise or MariaDB) helps by grouping threads.

MySQL's `max_connections` default is 151. The equivalent memory calculation uses `sort_buffer_size`, `join_buffer_size`, and `read_buffer_size` per connection instead of `work_mem`.

### Real-World Case Studies

An e-commerce platform set `max_connections = 2000` and `work_mem = 512MB` on a 64 GB server. During a flash sale, 800 concurrent connections each ran queries with hash joins, allocating multiple `work_mem` segments. The OOM killer terminated PostgreSQL at 800 connections. The fix: reduce `max_connections` to 200, set `work_mem = 32MB` globally, add PgBouncer with `default_pool_size = 80` and `max_client_conn = 3000`. The same flash sale load ran smoothly on 80 database connections with PgBouncer queuing excess requests. Peak memory usage dropped from 64 GB (OOM) to 28 GB.

## Source

- [PostgreSQL Connection Settings](https://www.postgresql.org/docs/current/runtime-config-connection.html)
- [PostgreSQL Memory Configuration](https://www.postgresql.org/docs/current/runtime-config-resource.html)
- Celko, J. "SQL for Smarties" (2010)

## Process

1. Calculate per-connection memory cost (base + work_mem + temp_buffers).
2. Set `max_connections` based on available RAM after shared_buffers and OS reservation.
3. Reserve connections for superuser and monitoring tools.
4. Deploy a connection pooler (PgBouncer) to multiplex application connections beyond the database limit.

## Harness Integration

- **Type:** knowledge -- this skill is a reference document, not a procedural workflow.
- **No tools or state** -- consumed as context by other skills and agents.
- **related_skills:** db-connection-pooling, db-acid-in-practice

## Success Criteria

- `max_connections` is derived from available RAM, shared_buffers, and per-connection memory cost -- not set to an arbitrary high number.
- `work_mem` is set conservatively at the server level and increased per-session only for analytical queries.
- Superuser and monitoring connections are reserved separately from application connections.
- Connection usage is monitored via `pg_stat_activity` and stays below 80% of `max_connections` during peak traffic.

Files in this skill

  • SKILL.md9.5 KB
  • skill.yaml743 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.