Why Does My Lambda Time Out Connecting to the Internet Inside a VPC?
You moved your Lambda function into a VPC to access an RDS instance or an ElastiCache cluster, and now every outbound HTTP call to an external API just hangs until the function times out. No error message, no connection refused — just silence. This is one of the most common Lambda networking traps, and the fix requires understanding why Lambda inside a VPC loses internet access entirely by default and what a NAT Gateway actually does in this architecture.
TL;DR: Lambda VPC Internet Access
| Scenario | Internet Access? | What's Required |
|---|---|---|
| Lambda NOT in a VPC | Yes | Nothing — AWS manages it |
| Lambda in VPC, public subnet | No | Lambda ENIs don't get public IPs; IGW alone is insufficient |
| Lambda in VPC, private subnet | No | NAT Gateway in a public subnet + route table entry required |
| Lambda in VPC, private subnet + NAT GW | Yes | NAT GW in public subnet, route 0.0.0.0/0 → NAT GW in private subnet route table |
How Lambda VPC Networking Works
When Lambda is not attached to a VPC, AWS places your function in a managed network environment that already has outbound internet access. You never see it, you never configure it — it just works. The moment you attach Lambda to a VPC, AWS creates Elastic Network Interfaces (ENIs) in the subnets you specify and routes all function traffic through your VPC's networking stack. From that point on, your Lambda function is subject to the same routing rules, security groups, and NAT constraints as any EC2 instance in those subnets.
The critical detail: Lambda ENIs are never assigned public IPv4 addresses, even if you place the function in a public subnet. An Internet Gateway only routes traffic for resources that have a public IP. Since Lambda ENIs have no public IP, the IGW has nothing to route — outbound packets from Lambda simply have no valid return path on the public internet.
Think of the Internet Gateway as a bouncer who only lets in guests on the list. Lambda's ENI has no public IP, so it's not on the list. The IGW won't forward its traffic regardless of which subnet it sits in.
This is why placing Lambda in a public subnet still doesn't restore internet access. The subnet being 'public' (i.e., having a route to an IGW) is a necessary but not sufficient condition — the resource itself needs a routable public IP, which Lambda ENIs never have.
Private Subnet"] -->|"outbound traffic
no public IP"| PrivateRT["Private Route Table
0.0.0.0/0 → NAT GW"] PrivateRT --> NATGW["NAT Gateway
Public Subnet + Elastic IP"] NATGW --> PublicRT["Public Route Table
0.0.0.0/0 → IGW"] PublicRT --> IGW["Internet Gateway"] IGW --> Internet["External API / Internet"] Internet -->|"return traffic
to Elastic IP"| NATGW NATGW -->|"translated back
to Lambda private IP"| LambdaFn
- Lambda in private subnet — ENI created in your private subnet. No public IP assigned.
- Private route table — Default route (0.0.0.0/0) must point to the NAT Gateway, not the IGW.
- NAT Gateway in public subnet — Receives Lambda's outbound traffic, translates the source IP to its own Elastic IP, and forwards to the IGW.
- Internet Gateway — Routes NAT Gateway traffic to the internet. The NAT GW has a public Elastic IP, so the IGW accepts it.
- Return traffic — Responses come back to the NAT GW's Elastic IP, which translates back to Lambda's private IP.
Why Lambda in a Public Subnet Still Times Out
This is the misdiagnosis that burns most engineers the first time. The assumption is: 'My subnet has a route to the IGW, so my Lambda should have internet access.' You check the subnet, confirm the IGW route exists, and still get timeouts. The assumption is wrong because internet access via IGW requires a public IP on the source ENI — and Lambda never gets one.
The observable symptom is always the same: the Lambda function reaches its configured timeout with no response, no DNS resolution error, and no TCP refused. The connection attempt just disappears. This happens because the outbound SYN packet leaves the ENI, hits the route table, gets forwarded toward the IGW, and the IGW drops it — there's no public IP to associate with the return traffic.
Placing Lambda in a public subnet is not just unhelpful — it's actively misleading because it looks correct from the console. The subnet has an IGW route. Security groups may be wide open. Everything looks right, and nothing works.
The Correct Architecture: NAT Gateway for Lambda Internet Access
The working pattern requires three components wired together correctly: Lambda in a private subnet, a NAT Gateway in a public subnet, and a route table entry in the private subnet pointing 0.0.0.0/0 at the NAT Gateway. Each piece is necessary; missing any one of them breaks the path.
Private IP only"] PrivRT["Route Table:
0.0.0.0/0 → nat-xxx"] end subgraph PubSub["Public Subnet"] NATGW["NAT Gateway
Elastic IP assigned"] PubRT["Route Table:
0.0.0.0/0 → igw-xxx"] end IGW["Internet Gateway
attached to VPC"] end ExtAPI["External API"] LambdaENI --> PrivRT PrivRT --> NATGW NATGW --> PubRT PubRT --> IGW IGW --> ExtAPI
- Private subnet — Where Lambda ENIs live. Route table has 0.0.0.0/0 → NAT Gateway.
- Public subnet — Where the NAT Gateway lives. Route table has 0.0.0.0/0 → Internet Gateway.
- NAT Gateway — Sits in the public subnet with an Elastic IP. Performs source NAT for all outbound private traffic.
- Internet Gateway — Attached to the VPC. Routes traffic from the NAT GW's Elastic IP to the internet.
- Security Group on Lambda — Must allow outbound traffic on the required ports (typically 443 for HTTPS APIs).
Step-by-Step: Diagnosing and Fixing Lambda VPC Internet Access
Step 1: Confirm Lambda is Actually Attached to a VPC
Start here because it's fast and eliminates the obvious. If Lambda isn't VPC-attached, the timeout has a different cause entirely — a misconfigured endpoint URL, a firewall on the destination, or a DNS issue unrelated to VPC networking.
aws lambda get-function-configuration \
--function-name your-function-name \
--query 'VpcConfig' \
--output json
If the output shows an empty VpcId and empty SubnetIds, Lambda is not in a VPC. Your timeout is caused by something else. If it shows populated values, continue to Step 2.
Step 2: Identify Which Subnets Lambda Is Using
You need the exact subnet IDs to inspect their route tables. The VPC config output from Step 1 gives you these, but pull them explicitly so you have them for the next commands.
aws lambda get-function-configuration \
--function-name your-function-name \
--query 'VpcConfig.SubnetIds' \
--output text
Step 3: Check the Route Table for Each Lambda Subnet
This is where the actual problem lives in most cases. You're looking for a 0.0.0.0/0 route that points to a NAT Gateway (target starts with nat-). If you see a route pointing to an IGW (igw-), or no default route at all, that's your problem — Lambda has no valid outbound path to the internet.
aws ec2 describe-route-tables \
--filters "Name=association.subnet-id,Values=subnet-xxxxxxxxxxxxxxxxx" \
--query 'RouteTables[*].Routes' \
--output table
Replace subnet-xxxxxxxxxxxxxxxxx with each subnet ID from Step 2. Run this for every subnet Lambda is attached to — Lambda may invoke in any of them, and a misconfigured route in even one subnet will cause intermittent timeouts that are difficult to reproduce.
Step 4: Verify a NAT Gateway Exists in a Public Subnet
A NAT Gateway must exist, must be in the AVAILABLE state, and must be in a subnet that itself has a route to an IGW. A NAT Gateway in a private subnet is a misconfiguration that silently fails — it has no path to the internet itself.
aws ec2 describe-nat-gateways \
--filter "Name=vpc-id,Values=vpc-xxxxxxxxxxxxxxxxx" \
--query 'NatGateways[*].{ID:NatGatewayId,State:State,Subnet:SubnetId}' \
--output table
If no NAT Gateway exists or all are in a DELETED/FAILED state, you need to create one. If one exists, note its ID and the subnet it's in, then verify that subnet's route table has 0.0.0.0/0 → IGW.
Step 5: Verify the Public Subnet's Route Table Has an IGW Route
The NAT Gateway's own subnet must route to the Internet Gateway — this is what makes it 'public.' If the NAT Gateway is sitting in a subnet with no IGW route, it cannot forward traffic to the internet regardless of how correctly everything else is configured.
aws ec2 describe-route-tables \
--filters "Name=association.subnet-id,Values=subnet-yyyyyyyyyyyyyyyyy" \
--query 'RouteTables[*].Routes' \
--output table
Replace subnet-yyyyyyyyyyyyyyyyy with the NAT Gateway's subnet ID from Step 4. Confirm a route exists with destination 0.0.0.0/0 and target starting with igw-.
Step 6: Add the Missing Route to the Private Subnet Route Table
If Step 3 showed no default route or an IGW route in the Lambda subnet's route table, add the correct route pointing to the NAT Gateway. First, get the route table ID associated with the Lambda subnet.
aws ec2 describe-route-tables \
--filters "Name=association.subnet-id,Values=subnet-xxxxxxxxxxxxxxxxx" \
--query 'RouteTables[*].RouteTableId' \
--output text
Then create the route:
aws ec2 create-route \
--route-table-id rtb-xxxxxxxxxxxxxxxxx \
--destination-cidr-block 0.0.0.0/0 \
--nat-gateway-id nat-xxxxxxxxxxxxxxxxx
If a conflicting 0.0.0.0/0 route already exists (pointing to an IGW), replace it:
aws ec2 replace-route \
--route-table-id rtb-xxxxxxxxxxxxxxxxx \
--destination-cidr-block 0.0.0.0/0 \
--nat-gateway-id nat-xxxxxxxxxxxxxxxxx
Step 7: Check Security Group Outbound Rules
Route table issues are the most common cause, but security groups can also block outbound traffic. Lambda's security group must allow outbound traffic on the ports your external API uses — typically TCP 443 for HTTPS. By default, security groups allow all outbound traffic, but this default can be removed.
aws ec2 describe-security-groups \
--group-ids sg-xxxxxxxxxxxxxxxxx \
--query 'SecurityGroups[*].IpPermissionsEgress' \
--output json
If the output is an empty array, all outbound traffic is blocked. Add an egress rule:
aws ec2 authorize-security-group-egress \
--group-id sg-xxxxxxxxxxxxxxxxx \
--protocol tcp \
--port 443 \
--cidr 0.0.0.0/0
Step 8: Create a NAT Gateway If One Doesn't Exist
If Step 4 found no usable NAT Gateway, you need to create one. NAT Gateways require an Elastic IP allocation. This step has cost implications — NAT Gateways incur hourly charges plus data processing charges. Check current pricing in the AWS documentation before proceeding.
# Allocate an Elastic IP
aws ec2 allocate-address \
--domain vpc
# Create the NAT Gateway in a public subnet
aws ec2 create-nat-gateway \
--subnet-id subnet-yyyyyyyyyyyyyyyyy \
--allocation-id eipalloc-xxxxxxxxxxxxxxxxx
NAT Gateway creation takes a few minutes. Poll until state is AVAILABLE before adding the route in Step 6:
aws ec2 describe-nat-gateways \
--nat-gateway-ids nat-xxxxxxxxxxxxxxxxx \
--query 'NatGateways[*].State' \
--output text
The Misdiagnosis Pattern: 'I Put Lambda in a Public Subnet'
Here's the failure pattern that shows up repeatedly in production: engineer attaches Lambda to a VPC for database access, notices internet calls break, reads that 'public subnets have internet access,' moves Lambda to a public subnet, and the timeouts continue. The console shows the subnet has an IGW route. The security group allows all outbound. Nothing is obviously wrong.
The actual cause: Lambda ENIs are never assigned public IPv4 addresses. The AWS documentation states this explicitly — Lambda functions connected to a public subnet cannot access the internet. The IGW route in the public subnet is irrelevant because the IGW only routes traffic for resources with associated public IPs, and Lambda's ENI has none.
The fix is always the same: move Lambda back to a private subnet and route outbound traffic through a NAT Gateway. Putting Lambda in a public subnet is not a valid workaround — it's a misconfiguration that wastes subnet IP space and still doesn't work.
IAM Permissions for This Diagnostic
Running the diagnostic commands above requires the following minimum IAM permissions. These are read-only for diagnosis; the create/replace route commands additionally require ec2:CreateRoute and ec2:ReplaceRoute.
🔽 Click to expand — IAM policy for VPC/Lambda network diagnostics
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:GetFunctionConfiguration",
"ec2:DescribeRouteTables",
"ec2:DescribeNatGateways",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeInternetGateways"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateRoute",
"ec2:ReplaceRoute",
"ec2:AllocateAddress",
"ec2:CreateNatGateway",
"ec2:AuthorizeSecurityGroupEgress"
],
"Resource": "*"
}
]
}
Note: ec2:Describe* actions require "Resource": "*" — resource-level restrictions are not supported for these actions per the AWS Service Authorization Reference.
VPC Endpoints as an Alternative for AWS Services
If your Lambda only needs to call AWS services (S3, DynamoDB, SQS, Secrets Manager, etc.) rather than arbitrary public internet endpoints, a VPC endpoint is a better solution than a NAT Gateway. VPC endpoints route traffic to AWS services through the AWS private network — no NAT Gateway, no IGW, no data processing charges for NAT.
Gateway endpoints (S3 and DynamoDB) are free. Interface endpoints (most other AWS services) have hourly and data processing charges, but are typically cheaper than NAT Gateway for AWS-service-only traffic patterns. If you need both AWS service access and public internet access, you still need a NAT Gateway — VPC endpoints don't replace it for arbitrary internet destinations.
Wrap-Up: Fixing Lambda VPC Internet Access
Lambda VPC internet access requires a specific three-layer path: Lambda ENI in a private subnet → NAT Gateway in a public subnet → Internet Gateway on the VPC. Every layer must be present and correctly wired. Placing Lambda in a public subnet does not work because Lambda ENIs never receive public IPs, making the IGW route irrelevant. The diagnostic sequence is: confirm VPC attachment → inspect subnet route tables → verify NAT Gateway existence and state → verify NAT Gateway's subnet has an IGW route → check security group egress rules.
For AWS-service-only connectivity needs, evaluate VPC endpoints before provisioning a NAT Gateway — they're simpler, often cheaper, and eliminate the routing complexity entirely.
Refer to the AWS Lambda VPC documentation and the NAT Gateway documentation for current configuration details and pricing.
Glossary
| Term | Definition |
|---|---|
| ENI (Elastic Network Interface) | A virtual network card attached to a resource in a VPC. Lambda creates ENIs in your specified subnets to connect functions to your VPC. |
| NAT Gateway | A managed AWS service that performs source network address translation, allowing resources in private subnets to initiate outbound internet connections without being directly reachable from the internet. |
| Internet Gateway (IGW) | A horizontally scaled VPC component that enables communication between resources with public IPs and the internet. Does not route traffic for resources without public IPs. |
| Route Table | A set of rules (routes) that determine where network traffic from a subnet is directed. Each subnet is associated with exactly one route table. |
| VPC Endpoint | A private connection between your VPC and supported AWS services that does not require an IGW, NAT Gateway, or public IP. Gateway endpoints (S3, DynamoDB) are free; interface endpoints incur charges. |
Comments
Post a Comment