IPv6 on Your VPS: What a /64 Gives You and How to Use It

Many VPS plans include an IPv6 /64 next to the IPv4 address. Often it sits unused: the server has it, but the website, the DNS and the firewall only know about IPv4. This guide explains what a /64 actually is, how to confirm it works, and the four steps to serve a site over IPv6 properly: the address, the AAAA record, the web server, and the firewall.
What a /64 means
An IPv6 address is 128 bits long. The "/64" says the first 64 bits are your network prefix, fixed by the provider. The last 64 bits are yours to choose. That is 2 to the power of 64 possible addresses, roughly 18 quintillion, in a single subnet.
That number sounds absurd, but it is not meant to be used up. A /64 is the standard size for one IPv6 network. Many IPv6 features, such as automatic address configuration (SLAAC), assume it. In practice it gives you three useful things:
A stable main address for the server, for example one ending in
::1.Extra addresses for free: a separate address per website, per service, or per container, without asking for more IPs.
Direct reachability: no NAT. Every address can be reached from the internet, which is also why the firewall matters.
In the examples below, 2001:db8:1234:5678::/64 stands for your subnet. The 2001:db8::/32 range is reserved for documentation, so replace it with the prefix shown in your control panel.
Step 1: check that IPv6 is configured
ip -6 addr show
ip -6 route show default
ping -6 -c 3 2606:4700:4700::1111
You want three things: an address with scope global (not only fe80:: link-local addresses), a default IPv6 route, and replies to the ping. If you only see fe80:: addresses, the subnet is assigned to the VPS but not configured inside the operating system.
On Ubuntu, IPv6 is set with netplan. A typical static setup looks like this; your interface name (eth0, ens3 and so on) and gateway come from ip link and your provider's details:
network:
version: 2
ethernets:
eth0:
addresses:
- 203.0.113.10/24
- 2001:db8:1234:5678::1/64
routes:
- to: default
via: 203.0.113.1
- to: default
via: fe80::1
nameservers:
addresses: [1.1.1.1, 2606:4700:4700::1111]
Many providers use fe80::1 as the IPv6 gateway, but not all. Use the gateway shown for your server. Apply the change with a safety net:
sudo netplan try
# press Enter to confirm within 120 seconds, or it rolls back
sudo netplan apply
netplan try rolls back automatically if you lose the connection, which protects you from locking yourself out over SSH.
Adding more addresses from the /64
To test an extra address without making it permanent:
sudo ip -6 addr add 2001:db8:1234:5678::2/64 dev eth0
To keep it after a reboot, add it to the addresses list in netplan. If the main address works from outside but an extra address does not, ask your provider how the /64 is delivered. Some setups route the whole subnet to your server; others expect each address to be announced on the local network.
Step 2: add an AAAA record
An A record maps a name to an IPv4 address. An AAAA record does the same for IPv6. At your DNS provider, add:
example.com. 3600 IN AAAA 2001:db8:1234:5678::1
www.example.com. 3600 IN AAAA 2001:db8:1234:5678::1
Keep the existing A records. Most visitors will reach you over whichever protocol works best for them. Check the result:
dig AAAA example.com +short
Add the AAAA record only after IPv6 works end to end on the server. Browsers usually fall back to IPv4 when IPv6 fails, but other clients, such as some API clients, monitoring tools and mail servers, may simply report an error. A broken AAAA record is worse than none.
Step 3: make Nginx listen on IPv6
A common surprise: the site works over IPv4, the AAAA record is right, and IPv6 visitors still get "connection refused". The usual cause is that the web server only listens on IPv4. In Nginx, listen 80; covers IPv4 only. You need a separate [::] line:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
# ssl_certificate, root, locations ...
}
Test and reload, then confirm the sockets:
sudo nginx -t && sudo systemctl reload nginx
sudo ss -tlnp | grep nginx
You should see both 0.0.0.0:80 and [::]:80 (and the same for 443). On Linux, Nginx opens the IPv6 socket as IPv6-only by default, which is why the two lines work side by side. The same idea applies to other software: if an application binds to 0.0.0.0, it is IPv4 only. Binding to :: is the IPv6 equivalent.
Step 4: firewall IPv6 too
This step is easy to miss and it matters. With IPv6 there is no NAT hiding your services. Every listening port on every address is reachable unless the firewall blocks it.
UFW: check that IPv6 support is on in /etc/default/ufw:
IPV6=yes
With that set, each rule you add covers both protocols:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status
The status list should show each rule twice, once with (v6).
iptables: IPv4 and IPv6 have separate rule sets. Rules in iptables do nothing for IPv6. A server with strict iptables rules and an empty ip6tables policy is fully open over IPv6. Check with:
sudo ip6tables -L -n -v
nftables: a table in the inet family covers both protocols with one rule set:
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
meta l4proto icmp accept
meta l4proto ipv6-icmp accept
tcp dport { 22, 80, 443 } accept
}
}
Do not block ICMPv6 completely. IPv6 relies on it for neighbor discovery (finding the gateway) and for path MTU discovery. Blocking it can make IPv6 fail in confusing ways, such as connections that open but then hang on large responses.
Step 5: test from both sides
From the server, check that outgoing IPv6 works:
curl -6 -s https://icanhazip.com
From another machine that has IPv6, test the site over each protocol separately:
curl -6 -I https://example.com
curl -4 -I https://example.com
Both should return the same status line, for example HTTP/2 200 or HTTP/1.1 301. To test a specific address directly, wrap it in brackets:
curl -6 -I "http://[2001:db8:1234:5678::1]/"
Typical errors and what they usually mean:
Could not resolve hostwith-6: no AAAA record yet, or it has not propagated.Connection refused: the address is reachable, but nothing listens on that port over IPv6. Check the[::]listen lines.Connection timed out: a firewall is dropping the traffic, or the address is not configured or routed.Network is unreachable: the machine you are testing from has no IPv6 route.
A note on email
If your server sends email and it has IPv6, the mail server may send over IPv6. Large mail providers check that the sending address has a matching reverse DNS (PTR) record and that it is covered by your SPF record (for example with an ip6: entry). Set both before sending over IPv6, or configure the mail server to send over IPv4 only.
Run it on IM HOST
Every IM HOST Linux VPS comes with a dedicated IPv4 and an IPv6 /64, full root access on KVM, and DDoS protection. Plans start at $5/month, with no setup fees and a 30-day money-back guarantee. You can register or manage the domain for your AAAA records through our domain names service, and our engineers are available 24/7 if your IPv6 setup needs a second pair of eyes.
More from our blog
Discover more practical guides and product insights from the IM Host team.
View all articles