How to Work With IPv6 in AWS and Don't Die in the Process

How to Work With IPv6 in AWS and Don't Die in the Process

Discover how to transition from IPv4 to IPv6 in AWS. This guide covers topics like IPv6 subnetting, public and private subnets, and DNS64 and NAT64.

Published Jul 8, 2024
Even though IPv6 has been around for a long time, the first draft was presented in 1998, it has not played a significant role in many networking architectures until now. Even though the main reason of existence of IPv6 is to address the IPv4 address exhaustion, the adoption of IPv6 has been delayed due to some temporary solutions like NAT. But now with the bloom of IoT and smart devices on one hand and the increasing prices of IPv4 addresses on the other, many companies have started to deploy IPv6.
Since 1st February 2024 AWS charges for any public IPv4 address even if they are attached to an ENI. This is a huge incentive to start deploying network architectures based on IPv6 in AWS. But, it isn’t very hard to transition from IPv4 to IPv6?. Not at all. In this article I’m going to show you some key aspects of IPv6 in AWS and a small reference architecture to work with this protocol version.


The first thing we must know about IPv6 is how subnetting works. IPv6 uses 128 bits addresses while IPv4 uses only 32 bits. The representation of this addresses is in the form of eight groups of four hexadecimal digits, each group representing 16 bits. The groups are separated by colons. An example of an IPv6 address is:
For simplicity, there are some rules that allow shorten the representation of an IPv6 Address.
  • Leading zeros in each field could be omitted.
  • The longest sequence of zeros could be compressed as double colons ::.
In our example address this would render as 2001:db8:85a3::8a2e:370:7334 which is way shorter.
Subnetting in IPv6 works in a similar way of IPv4 Classless Inter-Domain Routing (CIDR), but, instead of using 32 bits it uses 128 bits in the form <ipv6-address>/N where N is the number of bits representing the network part. For example, the address 2001:0db8:85a3::/64 uses the first 64 bits to set the network part, and the remaining 64 bits represent the host part ( 2^64 = 18446744073709551616264 hosts, which are a lot).
AWS uses the prefix /56 by default for VPC IPv6 CIDR ranges, but it could be in the range /44 to /60 in increments of /4. Subnets have a fixed sized of /64. This simplifies things a lot when designing networks with IPv6 in AWS.

Public and private subnets

Just like with IPv4, an IPv6 subnet is considered public if it has a route table with a route default ::/0 pointing to an Internet gateway. On the contrary, the subnet is considered private. But what happens with egress traffic in an IPv6 subnet? It depends, of course, on the type of subnet.
For IPv6 we could have two types of private subnets.
  • Dual-stack IPv4 and IPv6 subnets.
  • IPv6 only subnets.
Dual-stack subnets have an IPv4 CIDR and an IPv6 CIDR, therefore all ENIs in the subnet will receive an IP of each kind. IPv4 egress traffic will be routed through a NAT Gateway in a public subnet with a default route, and IPv6 egress traffic will be routed through an Egress Only Internet Gateway with a default route ::/0.
Dual-stack subnet with NAT Gateway
Dual-stack subnet with NAT Gateway

 Egress Only Internet Gateways work in a similar fashion as an Internet Gateway but with the difference that in only allows outbound traffic and not inbound traffic.
Dual-stack subnet with Egress Only Internet Gateway
Dual-stack subnet with Egress Only Internet Gateway
IPv6 only subnets only have an IPv6 CIDR and all the ENIs in the subnet will receive an IPv6 address. For this subnets, egress traffic is done through an Egress Only Internet Gateway but, this limits our destinations to be IPv6. Does this mean that we can’t reach IPv4 destinations? Not really, but more on this later.

Not all AWS services work with IPv6

At the time of writing this article, many AWS Services already support IPv6. Here is the list of AWS services that support IPv6. Nonetheless, there are still some AWS services that remain IPv4 only like AWS Systems Manager for example.
But, how do we do with those AWS services that are IPv4 only? Let’s find out.

IPv4 reachability from IPv6 only subnets

As we saw earlier, IPv6 only subnets allows IPv6 egress traffic with the use of an Egress Only Internet Gateway, but sometimes it is needed to access IPv4 destinations to the public internet.
For these use cases AWS offers two different technologies that combined together allow reach IPv4 destinations from IPv6 only subnets.
  • DNS64
  • NAT64


DNS queries to a DNS resolver could be (among many others) of type A for IPv4 records or type AAAA for IPV6 records. So, when you ask for a DNS record from an IPv6 only subnet, if the service you intend to access doesn’t support IPv6 you’ll receive an IPv4 address and won’t be able to connect. To solve this issue, AWS offers DNS64.
DNS64, when enabled at the subnet level, makes that every query sent to R53 VPC resolver returns either the content of the AAAA record, if there is at least one, or an IPv6 address synthesized by prepending the well-known /96 prefix, defined in RFC6052 (64:ff9b::/96), to the IPv4 address in the A record.
Your IPv6 only service then communicates with this address trough a NAT Gateway who will translate this address for us to an IPv4 one.


NAT64 it’s a feature of NAT Gateway that is already available in any existing or new NAT Gateway. It enables your IPv6 only service to communicate with IPv4 only services by translating the synthetized IPv6 address given by DNS64 into an IPv4 address.
Once enabled DNS64 in the subnet, your IPv6 only service sends traffic to a synthesized IPv6 address through the NAT gateway, the following happens:
  • From the 64:ff9b::/96 prefix, the NAT Gateway recognizes that the original destination is IPv4 and translates the IPv6 packets to IPv4 by replacing:
    • Source IPv6 with its own private IP which is translated to Elastic IP address by the Internet Gateway.
    • Destination IPv6 to IPv4 by truncating the 64:ff9b::/96 prefix.
  • The NAT Gateway sends the translated IPv4 packets to the destination through the Internet Gateway, Virtual Private Gateway, or Transit Gateway and initiates a connection.
  • The IPv4-only host sends back IPv4 response packets. After a connection is established, NAT Gateway accepts the response IPv4 packets from the external hosts.
  • The response IPv4 packets are destined for NAT gateway, which receives the packets and de-NATs them by replacing its IP (destination IP) with the host’s IPv6 address and prepending back 64:ff9b::/96 to the source IPv4 address. The packet then flows to the host following the local route.

A practical example

To see this in action I included a sample architecture that implements a VPC with a public dual-stack subnet, and a private IPv6 only subnet.
The architecture features NAT Gateway with NAT64 and DNS64 to access IPv4 destinations and Egress Only Internet Gateway for IPv6 destinations. For ingress traffic it features a dual-stack NLB in front of the test instance with an NGINX server.
The code of the example could be found here: https://gitlab.com/scambelo/aws-ipv6-demo
Once deployed, let’s see how this works in a practical way. From the EC2 Console connect to the test instance with Session Manger.

Accessing an IPv6 compatible service

Let’s try access a service that works with IPv6 like gitlab.com:
As we can see gitlab.com resolves to 2606:4700:90:0:f22e:fbec:5bed:a9b9 so the traffic is routed trough Egress Only Internet Gateway to reach the destination.

Accessing an IPv4 compatible service

Now we try to access a service that only works with IPv4 like github.com
We could observe that github.com resolves to 64:ff9b::4d0:1ac5. As we saw earlier, this address is prefixed by 64:ff9b::/96 and the latest 32 bits, 4d0:1ac5, correspond with the IPv4 address
In this case the traffic is routed trough NAT Gateway which after the required transformations communicates with the IPv4 service and returns the response to the instance.

Exposing a service from an IPv6 only subnet

But what about the communication in the other direction? Let’s see how behaves our solution based on NLB dual-stack.
First, we try to connect our NLB from an IPv4 only device:
As we can see, it resolves to a public IPv4 address and then reaches the NGINX even though it is located in an instance in an IPv6 only subnet.
And then we try to connect from an IPv6 origin (for this demonstration I used the same EC2 test instance but it could be any other host anywhere on the internet).
Now we see that it resolves both addresses 2a05:d018:edc:de00:fcd3:24a:53bd:eff1 and but chooses the IPv6 one rendering the same content without any issues.
The conclusion of this part is that we could also provide services from IPv6 only instances to be IPv6 and IPv4 compliance without a fuss.

Wrapping up

In this post we have seen what is IPv6, the differences with IPv4 and the problems that it solves. Also, we have seen how to implement a simple architecture with DNS64 and NAT64 to leverage IPv6 capabilities in our applications while maintaining IPv4 compatibility.
It is worth to mention that AWS also offers a comprehensive set of Dual Stack and IPv6-only Amazon VPC Reference Architectures. Check it out!.
Are you ready to start implementing IPv6?