The High Cost of Living in an IPv4 World
IPv4 addresses aren’t just scarce—they’re expensive. With major cloud providers now charging roughly $3.50 to $5.00 per month for a single public IPv4, a cluster of 20 small microservices can suddenly cost you an extra $1,000 per year just for the privilege of having an IP. Moving to an IPv6-only internal environment isn’t just a technical flex; it is a smart financial move.
I’ve deployed this setup in production environments handling millions of requests. It is rock solid. By pairing Jool—a kernel-level NAT64 implementation—with BIND9, you create a transparent gateway. Your IPv6-only servers can reach IPv4-only APIs, update repositories, and third-party services without even knowing a translation is happening.
Choosing Your Translation Weapon
Not all NAT64 tools are built the same. Before you copy-paste any commands, you need to decide which flavor of translation fits your architecture.
Stateless vs. Stateful NAT64
Stateless NAT64 (SIIT) is a simple 1:1 map. Every IPv6 address gets its own dedicated IPv4. While this is fast, it’s a bit pointless if your goal is saving addresses. It consumes IPv4s just as fast as a dual-stack setup.
Stateful NAT64 is what most of us actually need. It works like the traditional NAT (NAPT) in your home router. You can have 500 internal IPv6 servers sharing one public IPv4 address. The gateway tracks connections using a state table. Jool handles this inside the Linux kernel. This avoids the context-switching overhead of userspace tools like TAYGA, keeping your latency penalty well under 1ms.
How DNS64 Bridges the Gap
If NAT64 is the engine, DNS64 is the navigator. When your server tries to reach legacy-api.com, it asks for a AAAA record. If that API only supports IPv4, the DNS64 server takes the IPv4 address (like 93.184.216.34) and wraps it in a special prefix, usually 64:ff9b::/96. Your server then sends its packets to that synthesized IPv6 address, and Jool handles the heavy lifting of stripping the prefix and sending it back to the IPv4 internet.
The Trade-offs: Is It Production Ready?
Nothing in networking is free. Before you migrate, weigh these pros and cons based on my experience in the field.
- Pros:
- IPv4 Conservation: Run an entire data center on a handful of IPv4 addresses.
- Management Simplicity: No more overlapping private IPv4 subnets (10.0.0.0/8 exhaustion is real). You only manage one set of firewall rules.
- Future-Proofing: You’re already living in the future. When a service finally goes IPv6-only, you don’t have to change a thing.
- Cons:
- The Bottleneck: Your NAT64 gateway is a single point of failure. If it crashes, your cluster goes dark. High Availability (HA) via Keepalived or VRRP is mandatory.
- Payload Issues: Some ancient protocols like FTP or SIP embed IP addresses inside the data packets. These will break without specific helpers.
- Logging: Your web server logs on the IPv4 destination will only see the IP of your gateway, not the individual internal server.
The Implementation Guide
I recommend a dedicated Ubuntu 24.04 instance for your gateway. It needs a public IPv4 and a routed IPv6 prefix.
1. Install the Essentials
Jool lives in the kernel, so we need DKMS to keep it updated when your OS updates its kernel.
sudo apt update
sudo apt install -y dkms make gcc linux-headers-$(uname -r) bind9 jool-dkms jool-tools
Confirm the module is active:
sudo modprobe jool
lsmod | grep jool
2. Configure DNS64 (BIND9)
We need BIND to “lie” to our clients by synthesizing AAAA records. Edit /etc/bind/named.conf.options:
options {
directory "/var/cache/bind";
recursion yes;
allow-query { any; };
# The magic happens here
dns64 64:ff9b::/96 {
clients { any; };
};
dnssec-validation auto;
listen-on-v6 { any; };
};
Restart the service: sudo systemctl restart bind9.
3. Fire Up the NAT64 Engine
First, enable forwarding in the kernel so packets can actually move between interfaces.
sudo sysctl -w net.ipv6.conf.all.forwarding=1
sudo sysctl -w net.ipv4.conf.all.forwarding=1
Now, tell Jool which prefix to watch and which IPv4 address to use for the outside world. Replace 1.2.3.4 with your actual public IP.
# Create the instance
sudo jool instance add "default" --netfilter --pool6 64:ff9b::/96
# Map it to your public IP
sudo jool pool4 add "default" 1.2.3.4 --udp --tcp --icmp
4. The Moment of Truth
Go to your IPv6-only client. Update your DNS to point to the gateway’s IPv6 address. Then, try to reach an IPv4-only destination:
# This pings Google\'s DNS via the NAT64 prefix
ping 64:ff9b::8.8.8.8
# This tests the DNS64 synthesis
curl -I http://ipv4only.arpa
If you see a 200 OK, you’ve successfully tunneled through the legacy internet. It’s a great feeling to see an IPv6-only server pulling updates from an IPv4-only repo at full wire speed.
Final Verdict
Don’t let these settings disappear on reboot. Ensure you wrap your jool commands in a systemd unit or use /etc/jool/jool.conf. Moving to an IPv6-only architecture isn’t just about being modern. It’s about building a leaner, cheaper, and more scalable infrastructure that won’t break the bank as you grow.

