Get It Done in 5 Minutes: Enable IPv6 and Test Connectivity
If you just need to know whether your server has IPv6 and want to enable it as quickly as possible, follow these 3 steps:
# Check if IPv6 is enabled
cat /proc/sys/net/ipv6/conf/all/disable_ipv6
# Output: 0 = enabled, 1 = disabled
# Enable IPv6 immediately (no reboot required)
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=0
# Check assigned IPv6 addresses
ip -6 addr show
Seeing fe80:: in the output? Your server has received a link-local IPv6 address — but you’re not done yet. Link-local addresses only work within the same subnet; pings from the internet will still fail. You need a few more steps before the server is reachable from outside.
A Quick IPv6 Overview Before You Configure
The thing that trips people up when first working with IPv6: a single interface typically has 3–4 IPv6 addresses simultaneously, each serving a different purpose.
- Link-local (
fe80::/10): Auto-generated, only usable within the same subnet. Not routable outside. - Global unicast (
2000::/3): Public, globally routable address — the IPv6 equivalent of a public IPv4 address. - Loopback (
::1): Equivalent to127.0.0.1. - ULA (
fd00::/8): Internal private address, like192.168.x.xbut for IPv6.
When first deploying a VPS with IPv6, it’s easy to see fe80:: and think you’re done — but pings from outside still fail because that’s just a link-local address. You need a global unicast address to be reachable from the internet.
Configuring a Static IPv6 Address on Ubuntu/Debian
Ubuntu 20.04 and later manage networking through Netplan — configuration files live in /etc/netplan/, and /etc/network/interfaces is no longer used.
# View the current Netplan file
ls /etc/netplan/
cat /etc/netplan/00-installer-config.yaml
Edit the file to add a static IPv6 address:
# /etc/netplan/00-installer-config.yaml
network:
version: 2
ethernets:
eth0:
addresses:
- 192.168.1.100/24 # Static IPv4 (keep as-is)
- 2001:db8:1::100/64 # Static IPv6 (replace with your actual address)
routes:
- to: default
via: 192.168.1.1 # IPv4 gateway
- to: ::/0
via: 2001:db8:1::1 # IPv6 gateway
nameservers:
addresses:
- 8.8.8.8
- 2001:4860:4860::8888 # Google DNS IPv6
# Apply the configuration
sudo netplan apply
# Verify the result
ip -6 addr show eth0
ip -6 route show
Configuring a Static IPv6 Address on CentOS/RHEL/Rocky Linux
On CentOS/Rocky systems, interface configuration files are located in /etc/sysconfig/network-scripts/:
# Open the interface configuration file
sudo vi /etc/sysconfig/network-scripts/ifcfg-eth0
Add the IPv6 lines to the file:
TYPE=Ethernet
BOOTPROTO=none
NAME=eth0
DEVICE=eth0
ONBOOT=yes
# IPv4
IPADDR=192.168.1.100
PREFIX=24
GATEWAY=192.168.1.1
# IPv6
IPV6INIT=yes
IPV6_AUTOCONF=no
IPV6ADDR=2001:db8:1::100/64
IPV6_DEFAULTGW=2001:db8:1::1
DNS1=8.8.8.8
DNS2=2001:4860:4860::8888
# Restart network interface
sudo nmcli connection reload
sudo nmcli connection up eth0
# Or on older systems
sudo systemctl restart NetworkManager
Advanced Configuration: Dual-Stack and SLAAC
Dual-Stack: Running IPv4 and IPv6 in Parallel
This is the setup I run on all my production servers. IPv4 is kept for compatibility with legacy systems; IPv6 for direct connections without NAT — lower latency and none of the headaches that come with CGNAT.
One thing that’s easy to overlook: Nginx listening on :: (all IPv6) typically covers IPv4 as well through IPv4-mapped addresses — but only typically. The actual behavior depends on this kernel setting:
# Check whether IPv4-mapped addresses are enabled
cat /proc/sys/net/ipv6/bindv6only
# 0 = automatic dual-stack, 1 = must bind each protocol separately
SLAAC — Automatically Receiving an IPv6 Address from the Router
If your router supports RA (Router Advertisement), the server can automatically receive a global IPv6 address without any static configuration:
# Enable SLAAC for interface eth0
sudo sysctl -w net.ipv6.conf.eth0.autoconf=1
sudo sysctl -w net.ipv6.conf.eth0.accept_ra=1
# Write to /etc/sysctl.conf to make it persistent
echo "net.ipv6.conf.eth0.autoconf=1" | sudo tee -a /etc/sysctl.conf
echo "net.ipv6.conf.eth0.accept_ra=1" | sudo tee -a /etc/sysctl.conf
SLAAC is convenient for office workstations — no extra configuration needed. For servers, always assign a static IP. A fixed address makes writing firewall rules much simpler and you don’t have to worry about the IP changing after every reboot.
Configuring ip6tables — Firewall for IPv6
This is a mistake I’ve seen repeatedly when reviewing other people’s servers: iptables is locked down tight, but ip6tables is completely empty — IPv6 traffic flows straight through, unfiltered.
# View current ip6tables rules
sudo ip6tables -L -n -v
# Set up basic IPv6 rules
# Allow loopback
sudo ip6tables -A INPUT -i lo -j ACCEPT
# Allow established traffic
sudo ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow ICMPv6 (REQUIRED — without this, IPv6 will not work correctly)
sudo ip6tables -A INPUT -p icmpv6 -j ACCEPT
# Allow SSH
sudo ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
# Allow HTTP/HTTPS
sudo ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
# Drop everything else
sudo ip6tables -A INPUT -j DROP
# Save rules
sudo ip6tables-save | sudo tee /etc/ip6tables.rules
Never block ICMPv6 — this is a critical difference from IPv4. Partially blocking ICMPv4 and the server still runs fine. But ICMPv6 is the backbone of the entire protocol: neighbor discovery, router advertisement, and path MTU discovery all run through it. Block ICMPv6 and IPv6 breaks immediately.
Practical Tips from Real Network Management Experience
I manage networking for a 50-person office and a small datacenter — everything below comes from real mistakes, not theory:
1. Testing Connectivity the Right Way
# Ping IPv6 (use -6 to force IPv6)
ping6 google.com
# Or
ping -6 2001:4860:4860::8888
# IPv6 traceroute
traceroute6 google.com
# Curl over IPv6
curl -6 https://ipv6.google.com
# Check if DNS returns AAAA records
dig AAAA google.com
nslookup -type=AAAA google.com
2. Persistent sysctl — Keep IPv6 Enabled After Reboot
# Add to /etc/sysctl.conf or create a separate file
cat << 'EOF' | sudo tee /etc/sysctl.d/99-ipv6.conf
net.ipv6.conf.all.disable_ipv6=0
net.ipv6.conf.default.disable_ipv6=0
net.ipv6.conf.lo.disable_ipv6=0
EOF
# Apply immediately
sudo sysctl --system
3. Writing IPv6 Addresses in Nginx/Apache Configs
# Nginx — listen on both IPv4 and IPv6
server {
listen 80;
listen [::]:80; # IPv6 — square brackets required
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;
}
# SSH to a server over IPv6 (square brackets required)
ssh user@[2001:db8:1::100]
ssh -6 [email protected]
4. Troubleshooting When Your VPS Provider Assigns IPv6 but It Won't Connect
I ran into this exact situation on Vultr: IPv6 was showing in the dashboard, but pings to the outside were still timing out. The cause was that their IPv6 gateway required a different route configuration:
# Check IPv6 routes
ip -6 route show
# If the default route is missing, add it manually
sudo ip -6 route add default via 2001:db8:1::1 dev eth0
# Check neighbor cache (the IPv6 equivalent of IPv4's ARP)
ip -6 neigh show
If it's still not working, check the MTU. IPv6 requires a minimum of 1280 bytes — anything lower and packets are dropped silently, making it very difficult to debug:
ip link show eth0 | grep mtu
# If MTU < 1280, increase it
sudo ip link set eth0 mtu 1500
