You don't need NAT gateway to deploy Lambda into VPC
Remember that Wheeler Dealers episode where Ant converted a Maserati Biturbo to electric?
Bold move taking a classic V6 (pun intended) and swapping it for batteries and electric motors.
But the result was brilliant - same car, same look, but running cleaner and cheaper with modern technology
under the bonnet.
That’s exactly what we’re doing today with Lambda functions in VPCs.
VPC Dealers - The Electric Conversion
In this episode I’ve got a Lambda function that’s been deployed into a VPC for years. It needs VPC access to reach private resources like databases or internal APIs. But it also needs to call AWS APIs and maybe some external services on the Internet. The classic solution? Deploy to VPC private subnets with a route to NAT gateway in a public subnet.
It works. No question about it. But here’s the thing - NAT gateway charges you by the hour, every hour, whether you’re using it or not. For a Lambda function that might only run a few times a day, you’re paying for 24/7 infrastructure. It’s like leaving the engine running in the car park all day just in case you need to pop to the shops.
And before you ask - no, you can’t just deploy Lambda to a public subnet. Lambda functions in VPC never get a public IP address, even when deployed to public subnets. They always need NAT gateway or some other route to reach the Internet.
Classic Solutions
Now, there are traditional ways to reduce NAT gateway costs. You could use VPC endpoints for AWS services like S3 and DynamoDB to bypass the NAT gateway entirely for that traffic. You could consolidate multiple NAT gateways into one if you’re running several. You could even replace NAT gateway with a NAT instance on a small EC2 that you manage yourself.
All valid approaches. But they’re a bit like tuning the old engine - you’re still paying for something that sits there burning money even when idle. VPC endpoints help but only for specific AWS services. NAT instances save money but add operational overhead.
Why NAT Gateway Exists
Let’s step back and understand what NAT gateway actually does. With IPv4, you’ve got a limited pool of public IP addresses. Your VPC uses private IP addresses internally (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) that aren’t routable on the Internet. When your Lambda needs to reach the Internet, NAT gateway translates that private IP to a public IP address, sends the request out, receives the response, and translates it back to your private IP. It’s Network Address Translation (NAT). The gateway sits in a public subnet with an Elastic IP, acting as a middleman for all your outbound traffic.
IPv6 Changes Everything
Here’s the thing about IPv6 - there’s no address shortage. IPv6 has 2^128 (that’s 39 digits!) Every resource in your VPC can have a globally unique, routable IPv6 address. No translation needed.
When your Lambda function has an IPv6 address it can talk directly to the Internet. No middleman. No NAT gateway sitting there charging you by the hour. You just need an egress-only internet gateway. Like a regular Internet gateway, it’s a highly available regional service that scales automatically. It allows outbound IPv6 traffic while blocking inbound connections - keeping your Lambda in private subnet just as secure as it was behind the traditional NAT gateway.
Electric Conversion
So what does this conversion actually look like? I’ve put together a CloudFormation template that shows the key changes. Let’s walk through what’s different from a traditional IPv4 VPC setup.
First, I need to associate an IPv6 CIDR block with the VPC. AWS provides these for free, no need to worry about paying for public IPs like with IPv4.
IPv6CidrBlock:
Type: AWS::EC2::VPCCidrBlock
Properties:
VpcId: !Ref VPC
AmazonProvidedIpv6CidrBlock: true
Then subnets need IPv6 CIDR blocks. Notice AssignIpv6AddressOnCreation - this ensures Lambda
ENIs get IPv6 addresses automatically.
PrivateSubnet1:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.0.0/25
Ipv6CidrBlock: !Select [0, !Cidr [!Select [0, !GetAtt VPC.Ipv6CidrBlocks], 2, 64]]
AvailabilityZone: !Select [0, !GetAZs '']
AssignIpv6AddressOnCreation: true
Instead of a NAT gateway, I create an egress-only Internet gateway. This is attached to VPC like Internet gateway. No public subnets required.
EgressOnlyInternetGateway:
Type: AWS::EC2::EgressOnlyInternetGateway
Properties:
VpcId: !Ref VPC
Route table points IPv6 traffic (::/0) to the Egress-only gateway instead of a NAT gateway.
PrivateRouteIPv6:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationIpv6CidrBlock: ::/0
EgressOnlyInternetGatewayId: !Ref EgressOnlyInternetGateway
Security groups need to allow IPv6 egress traffic.
LambdaSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for Lambda function
VpcId: !Ref VPC
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
- IpProtocol: -1
CidrIpv6: ::/0
And finally, the Lambda function needs one crucial setting - Ipv6AllowedForDualStack: true.
This tells Lambda to use IPv6 for outbound connections:
MyLambdaFunction:
Type: AWS::Serverless::Function
Properties:
VpcConfig:
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
SecurityGroupIds:
- !Ref LambdaSecurityGroup
Ipv6AllowedForDualStack: true
That’s it. No public subnets. No NAT gateway. No Elastic IPs. Just private subnets with IPv6 and an Egress-only gateway. Same functionality, zero ongoing infrastructure costs.
Other Considerations
Almost all AWS services support IPv6 these days. Your Lambda can talk to S3, DynamoDB, API Gateway, SQS, SNS - all over IPv6. No issues there. And surprisingly, many Internet services also speak IPv6. Major CDNs, cloud providers, and APIs have had IPv6 support for years.
And if you’re exposing your own services to IPv4 clients through an ALB or CloudFront, dual-stack mode lets the load balancer handle IPv4 clients connecting to your IPv6 backend.
Your VPC resources can still use their private IPv4 addresses for internal communication. This is dual-stack mode - both IPv4 and IPv6 work simultaneously. Your Lambda talks to RDS using the same private IPv4 address it always has. Only outbound Internet traffic uses IPv6. This makes the conversion backward compatible and low risk.
Now, what about services that only speak IPv4? AWS offers NAT64 and DNS64 services that translate between IPv6 and IPv4. But here’s the catch - NAT64 gateway has the similar hourly charges as a regular NAT gateway. You’re back to paying for infrastructure. If you need to reach IPv4-only services, you might be better off keeping a traditional NAT gateway.
The sweet spot for this approach is Lambda functions that primarily call AWS services and APIs that support IPv6. For those workloads, you can eliminate NAT gateway costs entirely while keeping everything else exactly as it was.
Summary
In this episode of VPC Dealers we took a Lambda function running in a traditional VPC with NAT gateway and converted it to use IPv6 with an egress-only Internet gateway. Same functionality, same security posture, but without the hourly infrastructure charges. Just like Ant’s electric Maserati - modern technology under the same classic bonnet.
If you’ve got Lambda functions in VPCs that are costing you money in NAT gateway charges, give this approach a try. The template is on GitHub ready to deploy.
Until next time. Ta-Da!

