0x55aa
← Back to Blog

AWS RDS Proxy: Stop Letting Lambda Slowly Choke Your Database šŸ—„ļøāš”

•10 min read

AWS RDS Proxy: Stop Letting Lambda Slowly Choke Your Database šŸ—„ļøāš”

Picture this: it's Black Friday. Your serverless checkout API is handling 10x normal traffic. Lambda is scaling beautifully — hundreds of concurrent functions spinning up. You're feeling like an AWS genius.

Then the alerts hit. Database errors everywhere. Customers can't check out. Your RDS instance is completely unresponsive.

You didn't run out of compute. You ran out of database connections.

Welcome to the Lambda + RDS connection exhaustion problem — the serverless gotcha that nobody warns you about until you're staring at a dead database at 11 PM on your busiest day. šŸ˜…

Why Lambda and Databases Are a Terrible Couple šŸ’”

Traditional apps are simple. You have a Node.js server with a connection pool:

Server → Pool (max 10 connections) → RDS

Pool opens 10 connections. Reuses them forever. RDS is happy.

Lambda doesn't work that way. Lambda is stateless and ephemeral. Each invocation can be a fresh execution environment:

Lambda invocation #1   → opens DB connection → RDS
Lambda invocation #2   → opens DB connection → RDS
Lambda invocation #3   → opens DB connection → RDS
... (x500 concurrent) → opens 500 connections → RDS šŸ’€

In production, I've deployed an e-commerce backend where checkout, inventory, and order functions all connected to the same RDS MySQL instance. At moderate traffic, fine. At Black Friday scale? We hit 500+ concurrent Lambdas each trying to hold their own connection.

The RDS instance we were using? db.t3.medium. Maximum connections: 405.

Go ahead and do that math. I'll wait. šŸ™ƒ

The Numbers That Should Terrify You 😱

RDS connection limits by instance type (MySQL):

db.t3.micro    →   66 connections (!!!)
db.t3.small    →  150 connections
db.t3.medium   →  405 connections
db.t3.large    →  810 connections
db.r5.large    → 4,000 connections

Lambda's default concurrency limit per account: 3,000 functions.

Your db.t3.micro? That thing dies after 66 simultaneous Lambdas. Sixty-six. A weekend product launch can blow past that in seconds.

When RDS runs out of connections, it doesn't gracefully queue requests. It throws:

OperationalError: (1040, 'Too many connections')

And every Lambda that tries to connect gets that error. Game over.

Enter RDS Proxy: The Connection Bouncer Your Database Needs 🚪

AWS RDS Proxy sits between your Lambda functions and your RDS instance. It's essentially a managed connection pool in front of your database.

Without RDS Proxy:

500 Lambdas → 500 connections → RDS (dead šŸ’€)

With RDS Proxy:

500 Lambdas → RDS Proxy (maintains pool of 30 connections) → RDS (happy 😊)

RDS Proxy multiplexes thousands of Lambda connections into a small, controlled pool of actual database connections. Lambdas connect to the proxy almost instantly — the proxy figures out which pooled connection to route them to.

A serverless pattern that saved us: After enabling RDS Proxy on our checkout Lambda, we went from connection errors under load to zero connection errors. Same Lambda functions. Same RDS instance. Just the proxy in the middle.

Setting It Up (It's Surprisingly Simple) šŸ› ļø

Step 1: Create the Proxy

Via AWS Console or CLI:

aws rds create-db-proxy \
  --db-proxy-name checkout-proxy \
  --engine-family MYSQL \
  --auth '[{
    "AuthScheme": "SECRETS",
    "SecretArn": "arn:aws:secretsmanager:us-east-1:123:secret:rds-creds",
    "IAMAuth": "REQUIRED"
  }]' \
  --role-arn arn:aws:iam::123:role/rds-proxy-role \
  --vpc-subnet-ids subnet-abc subnet-def \
  --vpc-security-group-ids sg-12345

Notice: RDS Proxy uses AWS Secrets Manager for credentials. No more hardcoding database passwords in environment variables. This alone is worth it. šŸ”

Step 2: Update Your Lambda Connection String

// Before: Connect directly to RDS
const db = mysql.createPool({
  host: 'mydb.abc123.us-east-1.rds.amazonaws.com',
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: 'ecommerce',
  connectionLimit: 10,  // This was lying to you — Lambda doesn't pool like this!
});

// After: Connect through RDS Proxy
const db = mysql.createPool({
  host: 'checkout-proxy.proxy-abc123.us-east-1.rds.amazonaws.com',  // Proxy endpoint!
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: 'ecommerce',
  connectionLimit: 1,  // Yes, just 1. Proxy handles the pooling.
  ssl: { rejectUnauthorized: true },  // Required for proxy
});

That's it. Literally just change the host. Your Lambda code stays identical.

Step 3: Lock Down with IAM Auth (Bonus Security)

Instead of username/password, use IAM tokens:

const { Signer } = require('@aws-sdk/rds-signer');

const signer = new Signer({
  hostname: 'checkout-proxy.proxy-abc123.us-east-1.rds.amazonaws.com',
  port: 3306,
  region: 'us-east-1',
  username: 'lambda_user',
});

const token = await signer.getAuthToken();

const db = mysql.createPool({
  host: 'checkout-proxy.proxy-abc123.us-east-1.rds.amazonaws.com',
  user: 'lambda_user',
  password: token,  // IAM token instead of static password
  database: 'ecommerce',
  ssl: { rejectUnauthorized: true },
});

No database passwords in your code or environment variables. Your security team will love you. šŸŽ‰

The Cost Reality Check šŸ’°

RDS Proxy isn't free. Here's how pricing works:

RDS Proxy costs:

$0.015 per vCPU of your RDS instance per hour

db.t3.micro  (2 vCPU) → $0.03/hour  → ~$22/month
db.t3.medium (2 vCPU) → $0.03/hour  → ~$22/month
db.r5.large  (2 vCPU) → $0.03/hour  → ~$22/month

When architecting on AWS, I learned: The proxy cost is tied to the RDS instance's vCPU count, not your traffic volume. A db.t3.micro and a db.r5.2xlarge (8 vCPU) proxy have wildly different costs.

Is it worth it?

For our checkout service: absolutely. One failed Black Friday = way more than $22/month in lost revenue. For a hobby project with 10 users? Probably not. Use it when Lambda concurrency can realistically exceed your RDS connection limit.

Real math from our setup:

Before Proxy:
- RDS db.t3.medium: $65/month
- Occasional scale-up to db.r5.large during peaks: +$140/month
- Emergency on-call incidents: priceless (and painful) 😤

After Proxy:
- RDS db.t3.medium: $65/month (same, no more scaling up)
- RDS Proxy: +$22/month
- Black Friday incidents: $0

We actually downgraded our RDS instance after adding the proxy because we no longer needed the extra connections headroom. Net savings: positive.

Gotchas That Will Bite You 🪤

Gotcha #1: Pinning Kills Your Pool

RDS Proxy multiplexes connections — but only when queries are "safe" to share. Certain operations pin a connection to a specific Lambda:

-- These CAUSE pinning (bad for pooling efficiency):
SET SESSION variable = value;
PREPARE statement;
LOCK TABLES;

In production, I've deployed a function that was setting session variables for row-level security. Every invocation pinned a connection. The proxy was barely helping. Audit your SQL for session-level operations!

Gotcha #2: Connection Borrow Timeout

If all pooled connections are busy, new requests wait. Default timeout is 120 seconds — and then they fail. You can tune this:

aws rds modify-db-proxy \
  --db-proxy-name checkout-proxy \
  --idle-client-timeout 1800

But the real fix: set connectionTimeoutMillis in your Lambda to something reasonable (5-10 seconds) so it fails fast and returns an error rather than hanging.

Gotcha #3: Proxy Doesn't Work Without VPC

Your Lambda must be in the same VPC as your RDS instance to use the proxy. If you've been running Lambda outside VPC (faster, simpler), you'll need to move it inside VPC.

VPC Lambda cold starts are slower (~100-500ms extra). Not a dealbreaker, but factor it in. Pair this with Lambda's VPC sharing feature and it's manageable.

Gotcha #4: PostgreSQL Connection Count Still Matters for Superuser

Even with a proxy, PostgreSQL reserves connections for the postgres superuser. Don't use superuser credentials for application code — you might lock out DBAs during an incident. Create a dedicated app_user with just the permissions it needs.

When RDS Proxy Is Overkill 🤷

Don't blindly add it everywhere:

Skip RDS Proxy if you're using DynamoDB — DynamoDB is connection-free by design. This whole problem doesn't apply.

Skip it for low-concurrency Lambdas. If your function runs 5-10 times per second max, you'll never hit connection limits. The $22/month buys you nothing.

Skip it for batch/async workloads. Background jobs processing SQS queues typically don't have 500 concurrent invocations. Direct RDS connection is fine.

Use it for:

  • āœ… User-facing APIs that scale with traffic
  • āœ… Checkout flows, payment processing
  • āœ… Any Lambda that connects to RDS AND scales unpredictably
  • āœ… Multi-tenant systems where you want IAM-based auth per function

The Failover Bonus Nobody Advertises šŸŽ

Here's a benefit that surprised me: RDS Proxy dramatically improves RDS failover handling.

When your RDS primary fails and the read replica is promoted, normally your application sees a connection error for 20-40 seconds during DNS propagation. Every Lambda trying to connect during that window fails.

With RDS Proxy? The proxy handles the failover internally. Your Lambda connections stay alive on the proxy side. Failover time drops from 20-40 seconds to under 10 seconds — and most of that is invisible to your Lambdas.

For an e-commerce backend, that's the difference between a scary alert and a complete checkout outage.

Quick Checklist Before You Enable It āœ…

  1. Check your RDS connection limit:

    aws rds describe-db-instances \
      --db-instance-identifier my-instance \
      --query 'DBInstances[0].DBInstanceClass'
    # Then check MySQL: SHOW VARIABLES LIKE 'max_connections';
    
  2. Estimate your Lambda concurrency:

    aws cloudwatch get-metric-statistics \
      --namespace AWS/Lambda \
      --metric-name ConcurrentExecutions \
      --dimensions Name=FunctionName,Value=checkout \
      --start-time 2026-02-20T00:00:00Z \
      --end-time 2026-02-26T00:00:00Z \
      --period 3600 \
      --statistics Maximum
    
  3. Create Secrets Manager secret with your RDS credentials (Proxy requires it)

  4. Create IAM role for proxy to access Secrets Manager

  5. Create the proxy, associate it with your RDS instance

  6. Update Lambda env var DB_HOST to proxy endpoint

  7. Monitor connection count in CloudWatch after switching:

    • Metric: DatabaseConnections on your RDS instance
    • Should drop significantly compared to pre-proxy peak

TL;DR šŸ’”

Lambda scales horizontally. RDS has a fixed connection limit. Put thousands of Lambdas in front of a small RDS instance and you get connection exhaustion — the worst kind of production failure because it looks like random database errors, not an obvious scaling problem.

RDS Proxy fixes this by:

  • Pooling connections between your Lambdas and RDS
  • Multiplexing thousands of Lambda "connections" into a small pool of real connections
  • Bonus: IAM auth, faster failover, no more passwords in environment variables

Cost: ~$22/month for most instance sizes. Completely worth it for production user-facing APIs.

When architecting on AWS, I learned the hard way that "serverless" doesn't mean you've escaped all the classic scaling problems. You've just traded server capacity limits for different limits — connection counts, concurrency quotas, memory caps. RDS Proxy is how you make Lambda and relational databases actually coexist in production without 3 AM incidents.

Don't wait for Black Friday to find out your database connection limit the hard way. šŸŽƒ


Running Lambda + RDS in production? I'd love to hear how you're handling connection pooling. Connect on LinkedIn — sharing war stories is how we all get better at this.

Want to see how I structure serverless backends? Check out my GitHub for real patterns from production e-commerce systems.

Now go add that proxy before the next traffic spike! ā˜ļøšŸ—„ļø


P.S. If you're on Aurora Serverless v2, the connection pooling behavior is different — Aurora autoscales ACUs and handles connections more gracefully. But you still benefit from the IAM auth and failover improvements of RDS Proxy, so consider it anyway.

P.P.S. Check your RDS connection limit RIGHT NOW. I'll bet you don't know what it is off the top of your head. Most people don't — until it matters! šŸ•µļø