RDS Connection Pooling for a Small App: When You Actually Need It
A t3.micro RDS instance caps at ~85 connections. Here's when a small app needs pooling, and the pgbouncer-vs-RDS-Proxy call that actually matters.

Most small apps hit RDS connection limits long before they hit CPU or memory limits — and the fix is usually pooling in your app, not RDS Proxy. A db.t3.micro running PostgreSQL caps out around 85 connections by default (max_connections is derived from memory, roughly DBInstanceClassMemory / 9531392). A single serverless function or a chatty web dyno can burn through that in a few seconds of traffic. This post explains why it happens, the cheapest fix that works, and the narrow case where RDS Proxy earns its keep.
Why a small database runs out of connections first #
Postgres reserves memory per connection whether that connection is doing work or sitting idle. On a t3.micro with ~1 GB of RAM, the derived max_connections lands near 85, and a handful of those are already reserved for superusers and replication. So your app doesn’t actually get 85 slots — it gets closer to 75. That number sounds fine until you learn how modern app stacks open connections. Every worker process in your web server opens its own pool. Every background worker opens another. A Prisma or TypeORM default pool is often 10-ish connections per instance, and if you run three instances behind a load balancer, you’re already at 30 before a single serverless function joins the party.
The pathological case is serverless. Each concurrent function invocation may open a fresh connection, and under a traffic spike you can go from 5 connections to 200 attempted connections in the time it takes a marketing email to land. Postgres responds with FATAL: sorry, too many clients already, your app throws 500s, and the database itself looks perfectly healthy on every CPU and memory graph. This is a classic reason a t3-class instance feels ’randomly unreliable’ — the bottleneck is connection accounting, not compute. If you’re weighing serverless against long-lived containers partly for this reason, the trade-offs are laid out in Serverless vs Managed Containers.
The cheapest fix: pool inside your app #
For a long-lived server process — a container or a VM that stays running — you almost never need RDS Proxy. You need to cap your ORM’s pool size and stop assuming the defaults are safe. If you know you’ll run at most 4 app instances and your database allows ~75 usable connections, budget something like 10 connections per instance and leave headroom for migrations, admin sessions, and monitoring. The math is boring and it works.
# Prisma: cap the pool in your connection string
DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=20"
# node-postgres (pg): cap the pool in code
# const pool = new Pool({ max: 10, idleTimeoutMillis: 30000 })An idle timeout matters as much as the max. Connections that never close accumulate silently, so setting idleTimeoutMillis (or the equivalent) to release unused connections keeps your steady-state footprint honest. Do this and a t3.micro comfortably serves a real production workload for a small team — the same honest-sizing philosophy behind the stages of AWS infrastructure cost, where infra should stay cheap until traffic genuinely forces it upward.
When RDS Proxy actually earns the money #
RDS Proxy sits between your app and Postgres, multiplexing thousands of short-lived client connections onto a small pool of real database connections. It shines in exactly one scenario for a small team: serverless or highly bursty compute where you cannot cap connections at the source because each invocation is independent. If your Lambda functions or per-request workers can spike concurrency unpredictably, Proxy absorbs the spike so Postgres never sees more than a bounded number of real connections.
The catch is cost. RDS Proxy is billed per vCPU of your database instance per hour, and for a small instance that lands in the ballpark of $12-15/month — often more than the t3.micro database it’s protecting. For a two-container web app, that money buys you nothing you couldn’t get by setting a pool max. For a serverless API with unpredictable bursts, it’s the difference between reliability and random outages. Proxy also adds a small latency hop (typically low single-digit milliseconds) and manages failover connection draining, which can be a genuine availability win if you care about that. The decision is a real one, not a default — the same theme we hit in ingress topology is a decision, not a default.
How Vylara handles this for you #
When you connect the Vylara GitHub App and the Vylara agent analyzes your repo, it detects your database from your dependencies — seeing prisma, pg, psycopg2, typeorm, or sequelize in your manifest tells it you need PostgreSQL. It surfaces a plain ’Your app needs’ card showing a managed database at roughly ~$15/mo, and you choose ’Create a managed database’ or point at one you already have. You never touch a provisioning console.
The honest limit: sizing is a starting guess until your real traffic shapes it. Vylara provisions to your own AWS account on the first deploy — connecting the repo and merging the agent’s PR updates your code, but the cloud environment comes into existence when you approve that first deploy. After launch, the in-app infrastructure chat can read your service logs and metrics; if it spots too many clients already errors, it can diagnose the connection exhaustion and propose a fix, with any write action gated behind your explicit approval. If your instance is oversized or undersized for the connection load you’re actually seeing, the monitoring layer suggests scaling moves you apply with one click. You can read more about how this fits the broader AWS picture on the AWS pillar, and if databases without a dedicated DBA are your reality, pair this with an RDS backup strategy for teams without a DBA.
The short version: cap your pool first, reach for RDS Proxy only when serverless bursts make capping impossible, and let honest sizing — not fear — decide when you outgrow a t3 instance.
Review your cloud plan in Vylara, merge delivery changes as Git PRs, and deploy into your own AWS or Azure account when you’re ready.
Start freeFrequently asked questions
- How many connections can a db.t3.micro Postgres handle?
- A db.t3.micro RDS PostgreSQL instance derives max_connections from memory, landing around 85 total — but a few are reserved for superusers and replication, so an app realistically gets ~75 usable connections. Cap your ORM pool to stay well under that.
- Do I need RDS Proxy for a small application?
- Usually no. If your app runs as long-lived containers or VMs, set your ORM pool max (e.g. 10 per instance) and you'll avoid connection exhaustion for free. RDS Proxy mainly helps serverless or bursty workloads where each invocation opens its own connection and you can't cap them at the source.
- How much does RDS Proxy cost?
- RDS Proxy is billed per vCPU of your database instance per hour, which for a small instance is roughly $12-15/month — often more than the t3.micro database it protects. That's only worth it when unpredictable serverless bursts would otherwise crash your database.



