The situation
The team wants to launch a new application in a greenfield VPC. The aspirations:
- Pure IPv6 inside the VPC. No IPv4 addresses on ENIs, no RFC1918 allocation. Subnets are
/64IPv6 only. - Outbound to the IPv4 internet. Some third-party APIs the app must call are IPv4-only. The app has to reach them.
- Inbound from IPv4 clients. Customer devices on legacy networks still exist. A public endpoint must accept both.
- Interoperability with the existing IPv4 estate. On-prem and the rest of AWS still use RFC1918. The new VPC has to talk to them.
- Cost win. The project’s business case depends on not paying for a lot of IPv4 addresses.
The options:
- Dual-stack VPC (IPv4 + IPv6). Every subnet has both; everything works; you still pay for IPv4 and still burn address space. The safe choice.
- IPv6-only VPC with NAT64/DNS64 for IPv4-only destinations. Outbound traffic to IPv4-only external hosts goes through a NAT64 gateway; DNS64 generates AAAA records for IPv4-only names. Inbound IPv4 via an ALB or NLB with a dualstack mode.
- IPv4 egress via a proxy in a dual-stack VPC. Keep new VPC IPv6-only, but route IPv4-bound traffic via a shared egress VPC that is dual-stack. Variation of option 2.
What actually matters
Before deciding, it’s worth knowing what an IPv6-only VPC provides and what it doesn’t.
What works natively in IPv6-only mode:
- EC2 instances (most current-gen Nitro instance types); ENIs get IPv6 addresses from the subnet’s
/64. - ECS on EC2 (same as EC2 underlying). ECS on Fargate supports IPv6-only tasks.
- Lambda functions in a VPC support IPv6 endpoints via IPv6-only subnets (current support varies by runtime and feature).
- RDS supports IPv6 for many engines.
- ALB, NLB in dualstack or dualstack-without-public-ipv4 mode, taking IPv6 traffic and forwarding to IPv6 targets.
- VPC endpoints (interface and gateway), most service endpoints support IPv6 via
enableDnsSupportand dualstack endpoint names.
What requires IPv4 somewhere:
- Communicating with IPv4-only external services (inevitable; the whole reason NAT64 exists).
- Some AWS services that haven’t completed IPv6 support, the list shrinks every quarter but is non-empty. Check the dualstack-endpoint availability per service per Region.
- Clients that are IPv4-only (user devices on legacy networks).
- On-prem over Direct Connect or VPN, if the on-prem isn’t IPv6-ready.
The bridge for IPv6-only-to-IPv4-only is protocol translation. A NAT64-style translator turns an IPv6 packet from the VPC into an IPv4 packet going out to the IPv4 internet, using the IETF well-known prefix to encode the IPv4 destination inside an IPv6 address. The DNS64 companion synthesises AAAA records for names that only have A records, embedding the IPv4 address into the same prefix. The client thinks it’s talking IPv6 end-to-end; the translator does the work.
That translation has to happen somewhere along the egress path, and it has to be paired with a resolver that knows when to synthesise the addresses. A standalone gateway is one option; an existing NAT/DNS pair extended with translation modes is another. The mechanics are the same; what differs is how many moving parts the team operates.
For inbound IPv4 clients, dualstack load balancing closes the gap. A load balancer with both IPv4 and IPv6 addresses at the edge accepts traffic from either family and forwards to IPv6 targets inside the VPC. Public IPv4 still bills per hour, but one load balancer’s public IPv4 is cheaper than every workload instance having one.
What we’ll filter on
- Zero IPv4 inside the VPC, do ENIs have IPv4 at all?
- Outbound to IPv4-only internet, via NAT64 without app changes?
- Inbound from IPv4 clients, via dualstack edge without instance IPv4?
- On-prem reachability, does the IPv6 VPC reach RFC1918 on-prem without NAT?
- Cost win on public IPv4, does the architecture reduce billable public IPv4 hours?
The IPv6-only landscape
1. Dual-stack VPC. Every subnet has IPv4 + IPv6. Everything works; nothing new to learn; pay for IPv4 addresses. The safe incumbent.
2. IPv6-only VPC + NAT64/DNS64 for egress + dualstack ALB/NLB for ingress. The AWS-native v6-only shape. The target design.
3. IPv6-only VPC + shared dual-stack egress VPC. The new VPC is v6-only; IPv4-bound traffic routes to a shared egress VPC that handles IPv4 translation. Slightly more plumbing; better for large estates that already have centralised egress.
Side by side
| Option | No IPv4 inside | IPv4 outbound | IPv4 inbound | On-prem reachability | Cost win |
|---|---|---|---|---|---|
| Dual-stack VPC | ✗ | ✓ | ✓ | ✓ | ✗ |
| IPv6-only + NAT64/DNS64 | ✓ | ✓ (via NAT64) | ✓ (via LB) | If on-prem has v6 or via translation | ✓ |
| IPv6-only + shared egress | ✓ | ✓ (via shared VPC) | ✓ (via LB) | Via shared transit | ✓ |
The path a packet takes
The pick(s) in depth
IPv6-only VPC with NAT64 + DNS64 for egress, dualstack ALB for ingress, TGW with IPv6 for east-west. The design that earns the cost-win without forcing every downstream dependency to support IPv6.
The pieces:
1. VPC configuration. Create the VPC with an IPv6 CIDR (either an Amazon-provided /56 or your own BYOIP). Subnets are IPv6-only: /64 chunks, no IPv4 CIDR on them. aws ec2 create-subnet --vpc-id vpc-0abc --ipv6-native --ipv6-cidr-block 2001:db8:1234:a::/64. --ipv6-native flag removes the IPv4 requirement.
2. EC2 instances. Launch with --no-associate-public-ip-address (there is no IPv4 to associate) and verify the instance type supports IPv6-only. Instance metadata service (IMDSv2) supports IPv6 as of recent updates; older AMIs may need a small bootstrap to prefer the IPv6 IMDS endpoint.
3. Route 53 Resolver DNS64. Enable DNS64 on the subnets: aws route53resolver update-resolver-config --resource-id subnet-xyz --autodefined-reverse-flag DISABLE --dns64 ENABLED. When DNS64 is enabled, AAAA queries for names with only A records get generated AAAA responses in the 64:ff9b::/96 prefix.
4. NAT Gateway with NAT64 route. Deploy a NAT Gateway in a subnet; the subnet can be IPv6-only (the NAT Gateway itself has dualstack addressing: the IPv4 side with an Elastic IP, the IPv6 side understanding the 64:ff9b::/96 space). Route table: 64:ff9b::/96 → nat-xxxxx.
5. Egress-only Internet Gateway. For pure IPv6 destinations that don’t need NAT, add an egress-only IGW and route ::/0 → egress-only IGW. Outbound IPv6-only traffic goes here; IPv6-to-IPv4-mapped traffic via the 64:ff9b::/96 route takes the NAT64 path.
6. Dualstack ALB/NLB for inbound. For services that must accept IPv4 clients, create an ALB with IP address type dualstack (or dualstack-without-public-ipv4 in newer AWS settings). The ALB has public IPv4 + IPv6 addresses at the edge; inside the VPC, targets are IPv6. Caller IPv4 traffic terminates at the ALB; the ALB forwards to IPv6 targets. Only the ALB costs public IPv4 hours, not every instance.
7. TGW with IPv6. For east-west to other VPCs, the TGW supports IPv6 prefixes alongside IPv4. Attach the IPv6-only VPC to the TGW; the TGW learns the 2001:db8:1234::/56 and propagates to other attachments. If the other attachments are IPv4-only, no route is installed there for the IPv6 prefix; reachability is to other v6-capable VPCs only.
8. On-prem. If on-prem is IPv6-ready, Direct Connect private VIFs support IPv6 (separate BGP sessions for IPv4 and IPv6, or single session with multi-protocol BGP). If on-prem is IPv4-only, east-west between the v6 VPC and on-prem requires a translation somewhere, usually a dual-stack shared services VPC that bridges.
9. Service endpoints. Use the dualstack DNS names for AWS service endpoints (xxxx.dualstack.us-east-1.amazonaws.com or the newer PrivateLink dualstack endpoints), otherwise VPC endpoint resolution falls back to IPv4 and the instance can’t reach them.
A worked DNS resolution + egress
An IPv6-only EC2 instance runs curl https://ipv4-only-api.example.com/v1/data.
- Application calls
getaddrinfofor the hostname. - The resolver library asks the VPC resolver (which is the R53 Resolver with DNS64 enabled).
- The resolver checks AAAA first. Real answer: no AAAA record (it’s an IPv4-only service).
- The resolver checks A. Real answer:
A 192.0.2.5. - DNS64 builds a AAAA from the A record:
AAAA 64:ff9b::c000:0205(which is64:ff9b::192.0.2.5in dotted-decimal-embedded form). - Application sees an IPv6 answer. Opens an IPv6 TCP connection to
64:ff9b::c000:0205:443. - Packet hits the VPC route table. The
64:ff9b::/96route points at the NAT Gateway. - NAT Gateway sees inbound IPv6 in the NAT64 prefix. It extracts the embedded IPv4 destination (
192.0.2.5), uses its Elastic IPv4 as source, and sends an IPv4 TCP packet to192.0.2.5:443. - The IPv4 server receives a normal IPv4 connection from the NAT Gateway’s EIP. It has no idea IPv6 is involved.
- Return traffic follows the reverse path: IPv4 response to NAT Gateway, NAT Gateway translates to IPv6 with the original embedded destination address, packet back to the instance.
No application code changes. The instance thinks it’s speaking IPv6 end-to-end; the NAT Gateway silently bridges.
What’s worth remembering
- IPv6-only VPCs require
--ipv6-nativesubnets. The subnet has no IPv4 CIDR at all; ENIs launched into it don’t get IPv4 addresses. - NAT64 + DNS64 is the bridge for IPv4-only destinations. DNS64 generates AAAA records in
64:ff9b::/96; NAT64 (the NAT Gateway in this role) translates outbound to IPv4. Combined, IPv6-only clients reach IPv4-only services transparently. - Dualstack ALB/NLB serves IPv4 clients without v4 on instances. The load balancer has public v4 addresses; instances are v6-only; the LB is the translation point.
- Egress-only IGW handles pure IPv6 outbound.
::/0 → EIGWfor v6-to-v6 internet traffic;64:ff9b::/96 → NAT Gatewayfor v6-to-v4. - Not every AWS service supports IPv6 yet. The list is closing but non-empty; check dualstack endpoint availability per service per Region. Use PrivateLink dualstack endpoints where available.
- TGW + DX support IPv6. You can build a fully v6 multi-VPC + on-prem network if on-prem is v6-ready. If not, a dual-stack shared services VPC is the usual bridge.
- The cost win is real. Public IPv4 addresses bill per hour. Fewer addresses = real savings at scale. NAT Gateway costs still apply; the saving is in per-instance IPv4 allocations.
- Edge cases live in SDKs and legacy clients. Some SDKs don’t honour AAAA records well; some clients have IPv6 code paths that have never been production-exercised. Test thoroughly before cutting over a customer-facing service.
IPv6-only VPCs are not an experiment anymore; they’re a cost lever with a clear technical path. NAT64 and DNS64 close the gap where the external world still speaks only IPv4. Dualstack load balancers close the gap where clients still speak only IPv4. The work isn’t convincing AWS that IPv6 is the future, it clearly is, it’s finding the one or two services in your stack that haven’t caught up, and routing around them without losing the cost win that justified the project.