How Do I Test IPv6-Only Application Compatibility?

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.

Quick Start: Verify Your Network Connectivity

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.

Why Test IPv6-Only Compatibility?

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.

Understanding [NAT64/DNS64](nat64-dns64-explained)

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.

Creating IPv6-Only Test Environments

Apple provides a simple method to create an IPv6-only test network with NAT64/DNS64:

Prerequisites:

Setup steps:

  1. Connect Mac to internet via Ethernet
  2. Configure internet sharing with NAT64:
# 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"
  1. Verify NAT64 is active:
# Check for NAT64 prefix
dns-sd -G v6 ipv4only.arpa

# You should see 64:ff9b::192.0.0.170 or similar
  1. Connect test device to the Wi-Fi network
  2. Disable IPv4 on test device:

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
  1. Verify IPv6-only connectivity:
# 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.

Method 2: Linux IPv6-Only Network with Tayga NAT64

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

Method 3: Docker IPv6-Only Container

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

Method 4: Cloud Provider IPv6-Only Instances

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

Method 5: Public DNS64 Services (Quick Testing)

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).

Application Compatibility Checklist

Test your application against these critical compatibility requirements:

1. No Hardcoded IPv4 Addresses

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))

2. Use IP-Agnostic Network APIs

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");

3. Avoid IPv4 String Parsing Assumptions

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

4. Test DNS Resolution Behavior

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).

5. Localhost Resolution

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

6. Firewall and Security Rules

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

7. Load Balancers and Proxies

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

8. Database Connection Strings

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

9. Third-Party APIs and Services

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.

10. Logging and Monitoring

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).

Testing Procedures and Tools

Automated Testing Framework

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

Manual Testing Protocol

Follow this checklist when manually testing:

Phase 1: Environment Validation

Phase 2: Basic Functionality

Phase 3: Feature Testing

Phase 4: Performance Validation

Phase 5: Error Scenarios

Network Analysis Tools

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

Common Compatibility Issues and Solutions

Issue 1: "Connection Refused" Errors

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))

Issue 2: DNS Resolution Failures

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.

Issue 3: Slow Performance

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:

Issue 4: Certificate Validation Failures

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.

Issue 5: Websocket Connection Failures

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
});

Mobile App IPv6 Requirements (Apple App Store)

Apple's IPv6-only requirement (mandatory since June 2016) applies to all iOS apps:

Testing Your iOS App

Use Apple's NAT64 network as described in Method 1 above.

Key testing points:

APIs that work correctly:

Common rejections:

Testing Checklist for App Store Submission

Best Practices for Developers

  1. Use high-level networking libraries - They handle IPv4/IPv6 automatically (requests, urllib3, axios, OkHttp)
  2. Prefer hostnames over IP addresses - Always use DNS names in configuration
  3. Enable dual-stack by default - Listen on :: (all IPv6, includes IPv4 with dual-stack)
  4. Test early and often - Include IPv6-only tests in CI/CD pipeline
  5. Validate with test-ipv6.run - Quick network validation: https://test-ipv6.run
  6. Use getaddrinfo() - Proper API for IP-agnostic name resolution
  7. Handle both address families - Don't assume AF_INET, use AF_UNSPEC
  8. Update dependencies - Ensure all libraries support IPv6
  9. Monitor IPv6 metrics separately - Track IPv4 vs IPv6 performance and errors
  10. Document IPv6 support - Include IPv6 compatibility in documentation and release notes

Continuous Integration Testing

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

Validation and Monitoring

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

Conclusion

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.