Testing IPv6-only application compatibility is crucial for ensuring your applications work in modern networks where IPv4 is unavailable. As mobile carriers, enterprises, and cloud providers transition to IPv6-only infrastructure with NAT64/DNS64 for IPv4 interoperability, applications must function correctly without native IPv4 connectivity. This comprehensive guide covers creating IPv6-only test environments, compatibility testing procedures, common pitfalls, and best practices for developers.
Before creating an IPv6-only test environment, validate your current network capabilities:
Visit test-ipv6.run to check:
This baseline assessment helps you understand your starting point and validates whether your test environment successfully removes IPv4 connectivity.
IPv6-only networks are becoming increasingly common:
Apple App Store Requirement: Since June 2016, all iOS apps submitted to the App Store must support IPv6-only networks. Apps using IPv4-specific APIs or hardcoded IPv4 addresses will be rejected during review.
Mobile Carriers: Major carriers like T-Mobile USA, Reliance Jio, and others deploy IPv6-only networks with NAT64/DNS64 for IPv4 connectivity, reducing operational costs and conserving IPv4 addresses.
Cloud Providers: AWS, Google Cloud, and Azure increasingly offer IPv6-only instance options with significant cost savings (up to 50% on AWS for IPv6-only workloads).
Enterprise Networks: Organizations transitioning to IPv6-only infrastructure to simplify network management and eliminate IPv4 address exhaustion issues.
Government Mandates: U.S. federal agencies are required to have 80% of networked assets operating in IPv6-only environments by the end of FY 2025.
Testing IPv6-only compatibility ensures your application works for these growing user populations and meets platform requirements.
Before diving into testing, understand how IPv6-only networks reach IPv4-only services:
DNS64: A DNS service that synthesizes AAAA records (IPv6 addresses) for domains that only have A records (IPv4 addresses). When your application requests the IPv6 address of an IPv4-only service, DNS64 returns a synthetic IPv6 address using the 64:ff9b::/96 prefix.
NAT64: A gateway that translates between IPv6 and IPv4 packets. When your application connects to a DNS64-synthesized address like 64:ff9b::203.0.113.1, the NAT64 gateway translates it to IPv4 address 203.0.113.1.
Example flow:
Your App -> DNS64 Query for ipv4only.example.com
DNS64 -> Returns 64:ff9b::203.0.113.1 (synthesized from 203.0.113.1)
Your App -> Connects to 64:ff9b::203.0.113.1
NAT64 Gateway -> Translates to IPv4: 203.0.113.1
IPv4-only Server -> Receives connection
Understanding the NAT64/DNS64 mechanism is critical for IPv6-only application development. Learn more about NAT64 in our comprehensive guide.
Apple provides a simple method to create an IPv6-only test network with NAT64/DNS64:
Prerequisites:
Setup steps:
# Open System Preferences > Sharing
# OR use command line:
# Enable IPv6 forwarding
sudo sysctl -w net.inet6.ip6.forwarding=1
# Configure NAT64
sudo /usr/sbin/natd -interface en0 -use_sockets -same_ports -dynamic
# Start internet sharing with NAT64/DNS64
# System Preferences > Sharing > Internet Sharing
# Share from: Ethernet (or your internet source)
# To computers using: Wi-Fi
# Check "Internet Sharing"
# Check for NAT64 prefix
dns-sd -G v6 ipv4only.arpa
# You should see 64:ff9b::192.0.0.170 or similar
For iOS: No manual configuration needed - the network provides IPv6-only connectivity.
For macOS testing device:
# Disable IPv4 on Wi-Fi interface
sudo networksetup -setv4off Wi-Fi
# Check you only have IPv6 address
ifconfig en0 | grep inet
# Test DNS64 is working
host -t AAAA ipv4only.arpa
# Should return 64:ff9b:: prefix address
# Verify no IPv4
curl -4 https://api.ipify.org
# Should fail
# Verify IPv6 and NAT64 work
curl -6 https://api.ipify.org
# Should work even though ipify.org has IPv4
Advantages: Official Apple-recommended method, accurately simulates real carrier networks, includes DNS64/NAT64 automatically.
Limitations: Requires Mac with two network interfaces, only works for devices connected to the shared network.
For server applications, APIs, or Linux-based services:
Install required packages (Ubuntu/Debian):
sudo apt update
sudo apt install tayga bind9 radvd
# Or on RHEL/CentOS/Fedora:
sudo dnf install tayga bind radvd
Configure Tayga NAT64:
# Edit /etc/tayga.conf
sudo tee /etc/tayga.conf > /dev/null <<EOF
tun-device nat64
ipv4-addr 192.168.255.1
prefix 64:ff9b::/96
dynamic-pool 192.168.255.0/24
data-dir /var/spool/tayga
EOF
# Create data directory
sudo mkdir -p /var/spool/tayga
# Start Tayga
sudo systemctl enable tayga
sudo systemctl start tayga
Configure DNS64 (BIND9):
# Edit /etc/bind/named.conf.options
sudo tee -a /etc/bind/named.conf.options > /dev/null <<EOF
options {
dns64 64:ff9b::/96 {
clients { any; };
};
};
EOF
# Restart BIND
sudo systemctl restart bind9
Configure Router Advertisements for automatic IPv6 configuration:
# Edit /etc/radvd.conf
sudo tee /etc/radvd.conf > /dev/null <<EOF
interface eth0 {
AdvSendAdvert on;
prefix 2001:db8::/64 {
AdvOnLink on;
AdvAutonomous on;
};
RDNSS 2001:db8::1 {
AdvRDNSSLifetime 30;
};
};
EOF
# Start radvd
sudo systemctl enable radvd
sudo systemctl start radvd
Disable IPv4 on test interface:
# Remove IPv4 address
sudo ip addr flush dev eth0 proto kernel scope global
# Remove IPv4 routes
sudo ip route del default via <gateway> dev eth0
# Prevent IPv4 DHCP
sudo systemctl stop dhclient
Verify IPv6-only operation:
# Check addresses
ip -6 addr show dev eth0
# Verify no IPv4
ping -4 -c 1 8.8.8.8
# Should fail
# Verify NAT64 works
ping -6 -c 3 64:ff9b::8.8.8.8
# Should succeed
# Test DNS64
dig AAAA google.com
# Should return 64:ff9b:: prefixed addresses if google.com is IPv4-only
For containerized applications:
# Create IPv6-only Docker network
docker network create --ipv6 \
--subnet=2001:db8:1::/64 \
ipv6-only-net
# Run container without IPv4
docker run -it --rm \
--network ipv6-only-net \
--sysctl net.ipv6.conf.all.disable_ipv6=0 \
ubuntu:latest \
/bin/bash
# Inside container, verify IPv6-only
ip addr show
ping -6 google.com
Note: Docker's IPv6 support has limitations. Full IPv6-only requires disabling IPv4 stack entirely, which currently requires host networking:
# On IPv6-only host
docker run -it --rm --network host app:latest
AWS IPv6-only EC2:
# Launch IPv6-only instance (IMDS IPv6 required)
aws ec2 run-instances \
--image-id ami-xxxxx \
--instance-type t3.micro \
--ipv6-address-count 1 \
--subnet-id subnet-xxxxx \
--metadata-options HttpProtocolIpv6=enabled
# Configure NAT64 gateway
aws ec2 create-nat-gateway \
--subnet-id subnet-xxxxx \
--connectivity-type private
Google Cloud IPv6-only VM:
# Create IPv6-only VM instance
gcloud compute instances create ipv6-test \
--zone=us-central1-a \
--network-interface=stack-type=IPV6_ONLY,subnet=default
For quick testing without infrastructure setup:
Google Public DNS64:
# Configure DNS servers to:
# 2001:4860:4860::6464
# 2001:4860:4860::64
# On Linux/macOS
sudo tee /etc/resolv.conf > /dev/null <<EOF
nameserver 2001:4860:4860::6464
nameserver 2001:4860:4860::64
EOF
# Disable IPv4 temporarily
sudo ifconfig en0 inet 0.0.0.0 delete # macOS
sudo ip addr del <ipv4-address>/24 dev eth0 # Linux
# Test
curl -6 https://www.google.com
Important: This only provides DNS64, not NAT64. You still need IPv6 connectivity to the NAT64 gateway (64:ff9b::/96).
Test your application against these critical compatibility requirements:
Problem: Applications with hardcoded IPv4 addresses like 192.168.1.1 or 10.0.0.1 will fail on IPv6-only networks.
Test:
# Search codebase for IPv4 addresses
grep -r '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' .
# Or use more sophisticated regex
grep -rE '\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b' .
Solution: Use hostnames and let DNS resolve to appropriate address family, or use IP-agnostic APIs.
Example fix:
# Bad
socket.connect(("192.168.1.100", 8080))
# Good
socket.connect(("api.example.com", 8080))
Problem: Using IPv4-specific socket APIs (AF_INET) prevents IPv6 connections.
Test: Review all socket creation and network connection code.
Solution: Use dual-stack APIs that handle both protocols:
# Bad - IPv4 only
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("example.com", 80))
# Good - IP agnostic (prefers IPv6)
import socket
sock = socket.create_connection(("example.com", 80))
# Or handle both explicitly
for res in socket.getaddrinfo("example.com", 80, socket.AF_UNSPEC, socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
try:
sock = socket.socket(af, socktype, proto)
sock.connect(sa)
break
except OSError:
sock.close()
continue
// Node.js - Good (IP agnostic)
const net = require('net');
const client = net.connect({
host: 'example.com',
port: 80,
family: 0 // 0 = IPv4 or IPv6, 4 = IPv4 only, 6 = IPv6 only
});
// Java - Good (IP agnostic)
Socket socket = new Socket("example.com", 80);
// Java automatically handles IPv4/IPv6
// Or explicitly prefer IPv6
System.setProperty("java.net.preferIPv6Addresses", "true");
Problem: Code that validates or parses IP addresses assuming IPv4 format.
Test:
# Find IP address validation regex
grep -r 'split.*\.' . | grep -i ip
grep -r '\^[0-9].*\.[0-9]' .
Solution: Use proper IP address parsing libraries:
# Bad
def is_valid_ip(ip):
parts = ip.split('.')
return len(parts) == 4 and all(0 <= int(p) <= 255 for p in parts)
# Good
import ipaddress
def is_valid_ip(ip):
try:
ipaddress.ip_address(ip)
return True
except ValueError:
return False
Problem: Assuming A records are always returned, or not handling AAAA records.
Test:
# On IPv6-only network, test DNS queries
dig AAAA example.com
host -t AAAA example.com
# Test your app's DNS resolution
tcpdump -i any -n port 53
Solution: Always query both A and AAAA records (or use getaddrinfo which does this automatically).
Problem: Assuming localhost is always 127.0.0.1.
Test:
# Check what localhost resolves to
getent hosts localhost
# Should include ::1 (IPv6 loopback)
Solution: Use localhost hostname instead of hardcoded 127.0.0.1, or explicitly bind to both ::1 and 127.0.0.1.
# Bad
server.bind(("127.0.0.1", 8080))
# Good
server.bind(("localhost", 8080))
# Or bind to all interfaces (IPv4 and IPv6)
server.bind(("::", 8080)) # IPv6 with dual-stack
Problem: Firewall rules configured for IPv4 only.
Test:
# Check IPv6 firewall rules
sudo ip6tables -L -n
# Verify application ports accessible via IPv6
nc -6 -zv ::1 8080
Solution: Configure ip6tables/firewalld for IPv6 traffic:
# Allow HTTP/HTTPS on IPv6
sudo ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
# Or using firewalld
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
Problem: Load balancers or reverse proxies not configured for IPv6.
Test: Access application through load balancer from IPv6-only client.
Solution: Configure Nginx/HAProxy for IPv6:
# Nginx - Listen on IPv6
server {
listen 80;
listen [::]:80; # Add IPv6 listener
listen 443 ssl;
listen [::]:443 ssl; # Add IPv6 SSL listener
server_name example.com;
...
}
# HAProxy - Bind to IPv6
frontend http_front
bind :80
bind :::80 # Add IPv6 binding
bind :443 ssl crt /etc/ssl/cert.pem
bind :::443 ssl crt /etc/ssl/cert.pem # Add IPv6 SSL binding
Problem: Database connection strings with IPv4 addresses.
Test: Review all database configuration files.
Solution:
# Bad
postgresql://192.168.1.50:5432/mydb
# Good
postgresql://db.example.com:5432/mydb
# Or explicit IPv6
postgresql://[2001:db8::50]:5432/mydb
Problem: Dependencies or third-party libraries that don't support IPv6.
Test:
# Check if third-party services support IPv6
dig AAAA api.thirdparty.com
# Test from IPv6-only environment
curl -6 https://api.thirdparty.com
Solution: Verify all dependencies support IPv6, or ensure NAT64/DNS64 is available for IPv4-only services.
Problem: Log parsing or monitoring systems expecting IPv4 address format.
Test: Generate logs from IPv6-only environment and verify monitoring systems process them correctly.
Solution: Update log parsers and monitoring to handle IPv6 addresses (colon-separated hexadecimal).
Create automated tests to validate IPv6-only compatibility:
# test_ipv6_compatibility.py
import socket
import requests
import pytest
def test_api_accessible_ipv6():
"""Test API is accessible over IPv6"""
# Force IPv6
requests.packages.urllib3.util.connection.HAS_IPV6 = True
response = requests.get(
"https://api.example.com/health",
timeout=10
)
assert response.status_code == 200
def test_no_hardcoded_ipv4():
"""Verify no hardcoded IPv4 addresses in code"""
import re
import os
ipv4_pattern = r'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b'
for root, dirs, files in os.walk('./src'):
for file in files:
if file.endswith(('.py', '.js', '.java', '.go')):
with open(os.path.join(root, file), 'r') as f:
content = f.read()
matches = re.findall(ipv4_pattern, content)
# Filter out version numbers like 1.2.3.4
ips = [m for m in matches if not m.startswith('0.') and not m.startswith('255.')]
assert len(ips) == 0, f"Found hardcoded IPs in {file}: {ips}"
def test_dns_resolution_dual_stack():
"""Test DNS resolves both A and AAAA records"""
import dns.resolver
# Should get IPv6 address
answers = dns.resolver.resolve('example.com', 'AAAA')
assert len(answers) > 0
def test_socket_dual_stack():
"""Test socket creation works with IPv6"""
# Should work on IPv6-only network
sock = socket.create_connection(("google.com", 80), timeout=5)
assert sock is not None
sock.close()
Run tests in IPv6-only environment:
# In Docker IPv6-only container or IPv6-only VM
pytest test_ipv6_compatibility.py -v
Follow this checklist when manually testing:
Phase 1: Environment Validation
ip -4 addr showping -6 google.comhost -t AAAA ipv4only.arpa (should return 64:ff9b:: address)Phase 2: Basic Functionality
Phase 3: Feature Testing
Phase 4: Performance Validation
Phase 5: Error Scenarios
Packet capture for IPv6:
# Capture all IPv6 traffic
sudo tcpdump -i any -n ip6
# Capture application-specific traffic
sudo tcpdump -i any -n 'ip6 and port 443'
# Save for analysis
sudo tcpdump -i any -n ip6 -w ipv6_capture.pcap
Connection monitoring:
# Show active IPv6 connections
netstat -an | grep tcp6
# Or with ss
ss -6 -tan
# Monitor DNS queries
sudo tcpdump -i any -n port 53
Test reachability:
# Test specific IPv6 address
ping -6 2001:4860:4860::8888
# Test NAT64 translation
ping -6 64:ff9b::8.8.8.8
# Trace route via IPv6
traceroute -6 google.com
Symptom: Application cannot connect to services on IPv6-only network.
Diagnosis:
# Check if service listening on IPv6
netstat -an | grep ':::80'
# Test connection
telnet ::1 80
nc -6 ::1 80
Solution: Ensure servers bind to IPv6 addresses:
# Bind to all interfaces (dual-stack)
server.bind(("::", 8080))
Symptom: Cannot resolve hostnames, "Name or service not known" errors.
Diagnosis:
# Check DNS64 configuration
dig AAAA google.com
# Verify DNS servers
cat /etc/resolv.conf
Solution: Configure proper DNS64 servers or ensure application uses system resolver.
Symptom: Application works but very slow on IPv6-only network.
Diagnosis:
# Test latency
ping -6 -c 10 google.com
# Check for DNS delays
time host google.com
Solution:
Symptom: SSL/TLS errors on IPv6-only network.
Diagnosis:
# Test HTTPS connection
curl -6 -v https://api.example.com
Solution: Ensure certificate validation doesn't rely on IPv4 address SANs.
Symptom: WebSocket connections fail to establish.
Diagnosis:
# Test WebSocket endpoint
wscat -c wss://example.com/socket
# Check server configuration
Solution: Configure WebSocket server to accept IPv6:
// Node.js WebSocket server
const WebSocket = require('ws');
const wss = new WebSocket.Server({
host: '::', // Listen on IPv6
port: 8080
});
Apple's IPv6-only requirement (mandatory since June 2016) applies to all iOS apps:
Use Apple's NAT64 network as described in Method 1 above.
Key testing points:
APIs that work correctly:
NSURLSession - Fully compatibleCFNetwork - Fully compatibleWKWebView - Fully compatibleAVFoundation streaming - Fully compatibleCommon rejections:
192.168.1.1)AF_INET sockets directly:: (all IPv6, includes IPv4 with dual-stack)Integrate IPv6-only testing into CI/CD:
# .github/workflows/ipv6-test.yml
name: IPv6 Compatibility Tests
on: [push, pull_request]
jobs:
ipv6-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up IPv6-only network
run: |
# Disable IPv4
sudo sysctl -w net.ipv4.conf.all.disable_ipv4=1
- name: Install dependencies
run: |
pip install -r requirements.txt
- name: Run IPv6 compatibility tests
run: |
pytest tests/test_ipv6_compatibility.py -v
- name: Test application in IPv6-only mode
run: |
./scripts/test_ipv6_only.sh
Once deployed, continuously validate IPv6-only compatibility:
Health checks:
#!/bin/bash
# Health check from IPv6-only environment
# Test API endpoint
curl -6 -f https://api.example.com/health || exit 1
# Verify no IPv4 fallback
if curl -4 -f https://api.example.com/health 2>/dev/null; then
echo "ERROR: Service still accessible via IPv4"
exit 1
fi
echo "IPv6-only health check passed"
Monitoring metrics:
Alerting:
# Set up alerts for:
- IPv6 connection failures > 1%
- IPv6 latency > IPv4 latency + 50ms
- DNS resolution timeouts
- Certificate validation errors from IPv6 clients
Testing IPv6-only application compatibility is essential for modern applications. Start by verifying your current network capabilities at test-ipv6.run, then create an appropriate IPv6-only test environment using macOS NAT64 sharing, Linux with Tayga, Docker containers, or cloud provider IPv6-only instances.
Follow the application compatibility checklist to identify and fix common issues: hardcoded IPv4 addresses, IPv4-specific APIs, DNS resolution assumptions, and firewall configurations. Use automated testing frameworks to continuously validate IPv6-only compatibility, and integrate these tests into your CI/CD pipeline.
For mobile apps, particularly iOS apps targeting the Apple App Store, IPv6-only compatibility is mandatory. Test using Apple's recommended NAT64 network method and ensure all features work without IPv4 connectivity. Remember that while your backend services don't need native IPv6 support (NAT64 provides translation), your client applications must handle IPv6-only networks correctly.
By following these testing procedures and best practices, you'll ensure your applications work reliably for the growing population of users on IPv6-only networks, meet platform requirements, and prepare for the future of internet connectivity. Regular validation with tools like test-ipv6.run helps maintain compatibility as networks evolve.