serverless aws databases claude-curated

Putting AWS Lambda in front of a relational database (RDS, Aurora) is common but mismatched at the connection layer. Lambda scales horizontally without warning; relational databases have hard connection limits. The combination needs deliberate handling.

The connection-explosion problem

Each Lambda execution environment (container) holds its own database connection — there is no shared pool across containers. If your function runs at 1000 concurrent invocations and each opens one connection, you’ve asked the database for 1000 connections.

A typical PostgreSQL db.t3.medium allows ~85 connections by default. A db.r5.large allows a few hundred. Burst traffic exhausts the pool, new Lambdas hang on connect, downstream timeouts cascade. Worse: every cold start that fails to connect still bills you for the duration.

RDS Proxy

Amazon RDS Proxy is a managed connection pooler that sits between Lambda and the database. It maintains a warm pool of database connections and multiplexes Lambda requests across them.

Benefits:

  • Lambda concurrency decoupled from DB connection count.
  • Connection reuse across cold starts (Lambda connects to the proxy, not the DB).
  • IAM authentication support.
  • Failover times improved (proxy holds connections through DB failover).

Cost: per-vCPU-hour of the underlying DB instance. Usually worth it for any non-trivial Lambda + RDS workload.

VPC-attached Lambda

Lambda can attach to a VPC to reach private RDS. Things to know:

  • ENI cold start — historically attaching a Lambda to a VPC added ~10 s to cold start while an Elastic Network Interface was provisioned. Since the 2019 HyperPlane rework, ENIs are pre-attached at function configuration time and shared across invocations. Cold-start penalty is now essentially negligible.
  • No public internet by default — a VPC-attached Lambda loses the public NAT it had outside the VPC. To reach external APIs (Stripe, AWS public endpoints) you need a NAT Gateway in a public subnet plus a route, or VPC endpoints for AWS services.
  • NAT egress cost — NAT Gateway charges per GB processed. Chatty Lambdas calling external APIs through NAT can rack up surprisingly large bills.
  • DNS — the function uses VPC-resolved DNS. Make sure enableDnsHostnames and enableDnsSupport are on for the VPC.

Connection lifecycle in handler code

Two competing patterns:

Open in init, reuse across warm invocations

# module scope — runs in init phase
conn = psycopg2.connect(...)
 
def handler(event, context):
    with conn.cursor() as cur:
        ...

Pro: no per-invocation connect cost on warm calls. Con: idle connections accumulate; the DB sees them as active until the container is destroyed.

Open per invocation

def handler(event, context):
    conn = psycopg2.connect(...)
    try:
        ...
    finally:
        conn.close()

Pro: clean lifecycle. Con: every invocation pays connect latency (~50–200 ms for Postgres TLS).

The right answer is usually reuse + RDS Proxy — get the warm-path speed-up without breaking the database.

IAM authentication

Instead of putting a username/password in environment variables (or even Secrets Manager), use IAM database authentication. The function’s execution role generates a short-lived auth token (15 min) and passes it as the password. Benefits:

  • No long-lived credentials at rest.
  • Centralised access control via IAM.
  • Audit trail through CloudTrail.

Costs: tokens have a rate limit (~200 new connections per second per DB instance for IAM auth). Combine with RDS Proxy to amortise.

See also

References