Why Your Lambda Times Out Inside a VPC (And Why NAT Gateway Is the Fix)

Moving a Lambda function into a VPC is a common requirement for accessing private RDS instances or ElastiCache clusters — but it silently breaks outbound internet access, causing mysterious timeouts when your function tries to reach external APIs. Here is exactly why this happens and how to fix it correctly.

TL;DR

Scenario Internet Access? Why?
Lambda not in a VPC ✅ Yes AWS manages networking; Lambda runs in AWS-owned infrastructure with internet access by default.
Lambda in VPC, public subnet ❌ No Lambda ENIs do not get a public IP, so the Internet Gateway cannot route return traffic.
Lambda in VPC, private subnet, no NAT ❌ No No route to the internet exists from the private subnet.
Lambda in VPC, private subnet, with NAT Gateway ✅ Yes NAT Gateway in public subnet translates private IPs to its own public Elastic IP for outbound traffic.

The Root Cause: How VPC Networking Actually Works

When Lambda is not attached to a VPC, AWS runs it inside its own managed network infrastructure that already has outbound internet connectivity. The moment you attach Lambda to your VPC, AWS provisions an Elastic Network Interface (ENI) inside your specified subnets. From that point on, Lambda is subject to your VPC's routing rules — nothing more, nothing less.

The critical constraint: Lambda ENIs are never assigned a public IPv4 address, even if you place them in a public subnet. This is a hard platform behavior, not a configuration option. An Internet Gateway (IGW) can only route inbound traffic to resources that have a public IP. Since Lambda's ENI has only a private IP, the IGW has no way to deliver return packets — so outbound connections silently fail or time out.

Analogy: Think of your VPC like a corporate office building. The Internet Gateway is the main lobby door — it only lets people in if they have a registered visitor badge (public IP). Lambda's ENI is an internal employee with no visitor badge. To make a phone call outside the building, the employee must route through the receptionist (NAT Gateway) who has an external line. The outside world only ever sees the receptionist's number, never the employee's internal extension.

Architecture: The Correct Network Flow

graph LR Lambda["Lambda ENI (Private IP: 10.0.2.x)"] PrivSub["Private Subnet 10.0.2.0/24"] RT_Priv["Private Route Table 0.0.0.0/0 → NAT GW"] NAT["NAT Gateway (Elastic IP: 54.x.x.x)"] PubSub["Public Subnet 10.0.1.0/24"] RT_Pub["Public Route Table 0.0.0.0/0 → IGW"] IGW["Internet Gateway"] API["External API api.example.com"] Lambda --> PrivSub PrivSub --> RT_Priv RT_Priv --> NAT NAT --> PubSub PubSub --> RT_Pub RT_Pub --> IGW IGW --> API
  1. Lambda ENI is provisioned in the private subnet. It holds only a private IP (e.g., 10.0.2.x).
  2. The private subnet's route table has a default route (0.0.0.0/0) pointing to the NAT Gateway.
  3. The NAT Gateway lives in the public subnet and holds an Elastic IP (a real public IP).
  4. The public subnet's route table has a default route (0.0.0.0/0) pointing to the Internet Gateway.
  5. The Internet Gateway forwards the packet to the external API. Return traffic flows back through the same path in reverse.

Why a Public Subnet Alone Does NOT Work for Lambda

graph LR Lambda2["Lambda ENI (Private IP only)"] PubSub2["Public Subnet (has IGW route)"] IGW2["Internet Gateway"] Drop["❌ Packet Dropped (No public IP mapping)"] Lambda2 -->|"placed in"| PubSub2 PubSub2 -->|"route: 0.0.0.0/0"| IGW2 IGW2 -->|"Cannot map private IP"| Drop

Even if you place Lambda in a public subnet (one with a route to an IGW), the ENI has no public IP. The IGW performs a 1:1 NAT only for resources with an associated public IP or Elastic IP. Lambda ENIs qualify for neither. The packet leaves Lambda's ENI with a private source IP, hits the IGW, and the IGW drops it because it cannot map the private IP to any public address. This is the most common misconception engineers have when first moving Lambda into a VPC.

Step-by-Step: Correct VPC Setup for Lambda Internet Access

Step 1 — VPC and Subnet Layout

You need at minimum: one public subnet (for the NAT Gateway) and one private subnet (for Lambda). They must be in the same VPC.

🔽 CloudFormation: VPC, Subnets, IGW
AWSTemplateFormatVersion: '2010-09-09'
Resources:

  MyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true

  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MyVPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: us-east-1a
      MapPublicIpOnLaunch: true

  PrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MyVPC
      CidrBlock: 10.0.2.0/24
      AvailabilityZone: us-east-1a

  InternetGateway:
    Type: AWS::EC2::InternetGateway

  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref MyVPC
      InternetGatewayId: !Ref InternetGateway

Step 2 — NAT Gateway with Elastic IP

The NAT Gateway must be deployed in the public subnet. It requires an Elastic IP to represent it to the internet.

🔽 CloudFormation: Elastic IP and NAT Gateway
  ElasticIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

  NATGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt ElasticIP.AllocationId
      SubnetId: !Ref PublicSubnet

Step 3 — Route Tables

Two separate route tables are required. The public subnet routes to the IGW. The private subnet routes to the NAT Gateway.

🔽 CloudFormation: Route Tables and Associations
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVPC

  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable

  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVPC

  PrivateRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NATGateway

  PrivateSubnetAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet
      RouteTableId: !Ref PrivateRouteTable

Step 4 — Lambda VPC Configuration

Attach Lambda to the private subnet only. The security group must allow outbound HTTPS (port 443) to reach external APIs.

🔽 CloudFormation: Lambda Function with VPC Config
  LambdaSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Lambda outbound internet access
      VpcId: !Ref MyVPC
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0

  MyLambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: my-vpc-lambda
      Runtime: python3.12
      Handler: index.handler
      Role: arn:aws:iam::123456789012:role/MyLambdaExecutionRole
      Code:
        ZipFile: |
          import urllib.request
          def handler(event, context):
              response = urllib.request.urlopen('https://api.example.com/data')
              return {'statusCode': 200, 'body': response.read().decode()}
      VpcConfig:
        SecurityGroupIds:
          - !Ref LambdaSecurityGroup
        SubnetIds:
          - !Ref PrivateSubnet

Step 5 — IAM: Required Permissions for VPC ENI Management

Lambda's execution role must have permissions to create and manage ENIs. The AWS managed policy AWSLambdaVPCAccessExecutionRole covers this. Attach it to your role following least privilege — do not use AdministratorAccess.

aws iam attach-role-policy \
  --role-name MyLambdaExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole

Diagnosing the Problem: Quick Checklist

Check What to Verify
Subnet type Lambda must be in a private subnet (no direct IGW route).
Private route table 0.0.0.0/0 must point to the NAT Gateway ID (not IGW).
NAT Gateway location NAT Gateway must be in a public subnet (one with IGW route).
NAT Gateway state Status must be Available in the AWS Console (EC2 → NAT Gateways).
Security Group egress Lambda's SG must allow outbound traffic on the required port (e.g., 443).
Elastic IP attached NAT Gateway must have an Elastic IP allocated and associated.

Cost Consideration: NAT Gateway Is Not Free

NAT Gateway incurs hourly charges plus per-GB data processing fees. Pricing and exact limits vary — always check the official AWS VPC pricing page. For Lambda functions that only need to reach AWS services (S3, DynamoDB, Secrets Manager, etc.) and not the public internet, consider using VPC Endpoints instead — they eliminate the need for a NAT Gateway for those specific services and are often more cost-effective.

Alternative: VPC Endpoints for AWS Service Access

If your Lambda only calls AWS-native services (not arbitrary public APIs), a Gateway VPC Endpoint (for S3/DynamoDB) or Interface VPC Endpoint (for most other AWS services) routes traffic privately within the AWS network — no NAT Gateway, no internet required.

aws ec2 create-vpc-endpoint \
  --vpc-id vpc-0abc123456789def0 \
  --service-name com.amazonaws.us-east-1.s3 \
  --route-table-ids rtb-0abc123456789def0 \
  --vpc-endpoint-type Gateway

Glossary

Term Definition
ENI (Elastic Network Interface) A virtual network card provisioned in your VPC subnet. Lambda creates one per availability zone when VPC-attached.
NAT Gateway A managed AWS service that performs Network Address Translation, allowing resources with private IPs to initiate outbound internet connections.
Internet Gateway (IGW) A horizontally scaled VPC component that enables communication between your VPC and the internet. Requires a public IP on the resource for inbound routing.
Public Subnet A subnet whose route table contains a route to an Internet Gateway (0.0.0.0/0 → igw-xxx).
VPC Endpoint A private connection between your VPC and supported AWS services, bypassing the internet entirely.

Next Steps

Related Posts

Comments

Popular posts from this blog

EC2 No Internet Access in Custom VPC: Attaching an Internet Gateway and Fixing Route Tables

IAM User vs. IAM Role: Why Your EC2 Instance Should Never Use a User

EC2 SSH Connection Timeout: The Exact Security Group Rules You Need to Fix It