Cause #1: Lambda isn't attached to the same VPC as RDS
What's actually happening here is that your Lambda function runs in AWS's default VPC (or no VPC at all) while your RDS instance sits in a private VPC. Internet traffic can't reach a private RDS instance, and your Lambda has no network path to it. That produces net::ERR_CONNECTION_TIMED_OUT after the TCP handshake times out.
This is the most common cause I see, and the fix is straightforward:
- Open your Lambda function in the AWS Console.
- Go to Configuration → VPC.
- Click Edit.
- Select the VPC that contains your RDS instance (check the RDS console for its VPC ID).
- Pick subnets that can reach the RDS — ideally private subnets, but if you only have public ones, that works too as long as you read Cause #3.
- Choose a security group that allows outbound MySQL traffic (port 3306) to your RDS security group.
- Save and redeploy the function.
The reason step 3 works is that Lambda now gets an ENI inside that VPC with a private IP. It can then route to RDS using the VPC's internal network, bypassing the public internet entirely.
One gotcha: after you attach a VPC, your Lambda loses internet access unless you add a NAT gateway (see Cause #3). If your function also calls external APIs, that'll break next.
Cause #2: Security groups block the connection
So you've attached the VPC and still get timeouts. The next suspect is the security group on RDS. RDS security groups are stateful but default-deny inbound. If you haven't explicitly allowed traffic from your Lambda's security group on port 3306, the connection gets dropped silently — no RST packet, just silence. That's why you see a timeout instead of a connection refused.
Here's the fix:
- Find your RDS instance in the RDS console.
- Look at the VPC security groups listed under the instance details.
- Open each security group and go to the Inbound rules tab.
- Add a rule: Type
MySQL/Aurora, Port3306, Source — select the Lambda security group (or the security group ID you attached to the Lambda function).
It's tempting to set Source to 0.0.0.0/0 for testing, but that opens your database to the world. Don't. Use the Lambda SG ID — it's precise and secure.
Also check the Lambda's own security group's outbound rules. By default, Lambda SGs allow all outbound, but if someone tightened them, you need an outbound rule for port 3306 to the RDS SG.
Cause #3: No route to the internet or NAT for public subnets
Here's a scenario: Your RDS is in a public subnet (yes, that happens), and your Lambda is in the same public subnet. Still times out. Why? Because RDS in a public subnet isn't actually public — it has no public IP unless you enabled that (and you shouldn't). The Lambda's ENI can't reach it because the RDS's ENI is in a private IP space, and the Lambda might be using a default route that doesn't include the VPC CIDR.
More often, the issue is that your Lambda is in a private subnet with no NAT gateway, and your function does something like fetch a secret from Secrets Manager or call an external service before the DB connection. That call times out, and the DB code runs after a long delay, making you think it's the DB.
The fix here depends on your setup:
- If RDS is in a private subnet and Lambda in a private subnet, make sure they're in the same VPC and the route tables include local routes (they automatically do).
- If Lambda needs internet (for Secrets Manager or other APIs), add a NAT gateway in a public subnet and update the private subnet's route table to point 0.0.0.0/0 to the NAT GW. Costs about $0.045/hour, so it's not free.
- Alternatively, use VPC endpoints for Secrets Manager and S3 — cheaper and no NAT needed.
I've debugged countless Lambda timeouts that weren't the DB at all — it was the function trying to reach the AWS API for a secret first. So check your function's code: does it call boto3 or SDK functions before the MySQL connection? If yes, that's often the bottleneck.
Quick-reference summary
| Cause | Symptom | Fix |
|---|---|---|
| Lambda not in VPC | Timeout immediately when trying to connect to any RDS endpoint | Attach Lambda to same VPC as RDS |
| Security group blocks port 3306 | Timeout after several seconds, can connect from a bastion host | Add inbound rule on RDS SG allowing Lambda SG on port 3306 |
| No NAT / wrong route | Lambda has VPC, but also calls external services and times out | Add NAT gateway or VPC endpoints for required services |
Remember, net::ERR_CONNECTION_TIMED_OUT in a browser usually surfaces when you test an API call that hits Lambda. The timeout might be your API Gateway idle timeout (29 seconds) or Lambda's own timeout (default 3 seconds, check that too). Set your Lambda timeout to at least 10 seconds during testing so you actually get a useful error instead of a canned timeout.