Your server almost certainly has an IPv6 address right now, and if your firewall rules only cover IPv4, every service you thought was locked down might be sitting open on the other protocol. That's the real cost of treating IPv6 as "something for later". Here's what actually differs between the two, and how to check, configure and debug both on Ubuntu and Rocky Linux.
| Address size | IPv4: 32-bit (about 4.3 billion). IPv6: 128-bit (about 3.4 x 1038) |
| Looks like | 192.168.1.10 vs 2001:db8:10::25 |
| Address assignment | IPv4: DHCP or static. IPv6: SLAAC, DHCPv6 or static |
| Linux check command | ip -4 addr and ip -6 addr |
The Short Answer: Same Job, Much Bigger Address Book
IPv4 and IPv6 do the exact same job. They give every device an address and get packets from point A to point B. The difference is mostly about how many addresses exist, and a bunch of cleanup the designers did while they were at it.
IPv4 uses 32-bit addresses, so the whole internet gets roughly 4.3 billion of them. That ran out years ago, which is why your home router hides a dozen devices behind one public IP using NAT, and why cloud providers now charge you for public IPv4 addresses. IPv6 uses 128-bit addresses. The number is so big it stops being meaningful, a single standard subnet (a /64) holds more addresses than the entire IPv4 internet, squared.
The thing people miss: the two protocols don't talk to each other directly. An IPv6-only client can't open a connection to an IPv4-only server without some translation box in the middle. So for the foreseeable future, servers run both side by side, which is called dual stack. If you want the wider picture of where IP sits next to TCP, UDP and friends, our guide on networking protocols explained covers the layers.
Key Takeaway:
How They Actually Differ Under the Hood
Most explanations stop at "IPv6 has more addresses" and call it a day. That's true but it doesn't help you much when ip addr shows three different IPv6 addresses on one interface and you've no idea where they came from. So let's go a bit deeper.
Address format and notation
An IPv4 address is four decimal numbers separated by dots, like 203.0.113.7. IPv6 is eight groups of four hex digits separated by colons: 2001:0db8:0010:0000:0000:0000:0000:0025. Nobody types that. You drop leading zeros in each group and replace one run of all-zero groups with ::, so it becomes 2001:db8:10::25. You can only use :: once per address, otherwise it's ambiguous.
Some addresses you'll see constantly on Linux: ::1 is loopback (the IPv6 version of 127.0.0.1), anything starting with fe80:: is link-local, and 2000::/3 is the global unicast range, i.e. your real public addresses.
The header got simpler
The IPv4 header is variable length (20 to 60 bytes) and carries a checksum that every router has to recalculate at every hop. IPv6 uses a fixed 40-byte header, drops the checksum entirely (TCP and UDP already do that work), and moves optional stuff into extension headers. Routers also no longer fragment packets. If a packet is too big, the router drops it and sends an ICMPv6 "Packet Too Big" message back so the sender can shrink it. Remember that one, it comes back in the troubleshooting section. The full spec is in RFC 8200 if you're curious.
How a host gets its address
With IPv4 you either set a static IP or a DHCP server hands one out. IPv6 adds SLAAC (Stateless Address Autoconfiguration). The router sends Router Advertisements with the network prefix, and the host builds its own address from that prefix. DHCPv6 still exists for networks that want central control, but plenty of networks never run it.
ARP is gone too. IPv6 uses Neighbor Discovery Protocol (NDP), which rides on ICMPv6. That's the big practical reason you can't just block all ICMPv6 like some admins used to do with ICMP on IPv4. Break it and neighbours can't find each other.
Where NAT fits (or doesn't)
Every device can have a globally routable IPv6 address, so NAT isn't needed. Practitioners on Reddit bring this up a lot, usually the worry is "so all my devices are exposed now?". Not quite. The stateful firewall on your router or server is what blocks unsolicited inbound traffic, not NAT. NAT just happened to have a similar side effect. On a server though, no NAT means no accidental hiding, so your firewall rules are doing all the work.
Concept:
Every IPv6-enabled interface gets a link-local fe80:: address automatically, even with no router and no config at all. That's why a box you think is "IPv4 only" still shows an inet6 line in ip addr. Link-local addresses never leave the local segment, but they are used for NDP and router discovery, so leave them alone.
Checking and Configuring IPv4 and IPv6 on Linux
Enough theory. These are the commands you'll actually run: see what addresses you have, set a static IPv6 address, make sure the firewall covers both protocols, and verify it all works. If any of the basic tools here are new to you, the essential Linux networking commands guide is a good warm up.
See what you already have (any distro)
Start here before changing anything. Plenty of VPS images come with IPv6 already live.
LinuxTeck
ip -4 addr show
# IPv6 addresses only (look for "scope global")
ip -6 addr show
# Routing tables for each protocol
ip -4 route show
ip -6 route show
# Is IPv6 disabled at kernel level? 0 means enabled
sysctl net.ipv6.conf.all.disable_ipv6
Set a static IPv6 address on Ubuntu 24.04
Ubuntu uses Netplan. Your file name may differ, check /etc/netplan/ first. Most cloud providers give you a whole /64 for free with the server, for example Vultr assigns one to every instance (we went through their network setup in our Vultr review), so you'll usually just copy the address and gateway from the control panel.
LinuxTeck
network:
version: 2
ethernets:
ens3:
dhcp4: true
dhcp6: false
accept-ra: true
addresses:
- '2001:db8:10::25/64'
routes:
- to: default
via: '2001:db8:10::1'
nameservers:
addresses: ['2606:4700:4700::1111', '1.1.1.1']
LinuxTeck
sudo netplan try
# Apply for real
sudo netplan apply
# UFW only filters IPv6 if this says IPV6=yes
grep IPV6 /etc/default/ufw
# Rules you add now apply to both IPv4 and IPv6
sudo ufw allow 22/tcp
sudo ufw status verbose
Set a static IPv6 address on Rocky Linux 9 and 10
Rocky uses NetworkManager, so it's nmcli instead of YAML. The nice part is firewalld handles IPv4 and IPv6 in the same zone, so one rule covers both.
LinuxTeck
nmcli connection show
# Set address, gateway and DNS in one atomic command
sudo nmcli connection modify ens18 ipv6.method manual ipv6.addresses "2001:db8:10::25/64" ipv6.gateway "2001:db8:10::1" ipv6.dns "2606:4700:4700::1111"
# Bring the connection back up with the new settings
sudo nmcli connection up ens18
# firewalld zones cover both protocols
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
Here's why the firewall part matters more than the address part. I once had Redis on a fresh Ubuntu VPS with iptables rules that looked perfect, port 6379 only from the app server. Then a scan from outside found it wide open. The provider had assigned a public IPv6 address, Redis was listening on [::], and every rule I'd written was iptables, not ip6tables. Took me an embarrassingly long time to spot. If you hand-write rules, you write them twice, or you move to nftables with the inet family which covers both.
Verify both protocols from the command line
Once things are configured, test each protocol on its own. Don't just test "the internet works", because your system will happily fall back to IPv4 and hide a broken IPv6 path.
LinuxTeck
ping -4 -c 4 linuxteck.com
ping -6 -c 4 google.com
# What public IP does the world see on each?
curl -4 -s https://ifconfig.co
curl -6 -s https://ifconfig.co
# Which services listen on IPv4 (0.0.0.0) vs IPv6 ([::])
sudo ss -tlnp
# Does a host publish an IPv6 address (AAAA record)?
dig AAAA google.com +short
# Path and MTU check over IPv6
tracepath -6 google.com
Compatibility Note:
ping6 and traceroute6 still exist on most systems but newer iputils merged them, so ping -6 is the portable form on both Ubuntu and Rocky. On minimal Rocky installs dig needs sudo dnf install bind-utils, on Ubuntu it is in dnsutils.
IPv4 vs IPv6: The Full Side-by-Side
Here's everything in one place. The verdict column is the practical call for someone running Linux servers, not a textbook ranking.
| Feature | IPv4 | IPv6 | Verdict |
|---|---|---|---|
| Address length | 32-bit | 128-bit | IPv6 wins |
| Total addresses | ~4.3 billion (exhausted) | ~3.4 x 1038 | IPv6 wins |
| Notation | Dotted decimal 192.0.2.1 |
Hex with colons 2001:db8::1 |
IPv4 easier to read |
| Header | 20 to 60 bytes, has checksum | Fixed 40 bytes, no checksum | IPv6 simpler for routers |
| Fragmentation | Sender or routers | Sender only (needs ICMPv6 PMTUD) | Don't block ICMPv6 |
| Address config | Static or DHCP | SLAAC, DHCPv6 or static | More options, more to learn |
| Neighbour lookup | ARP | NDP over ICMPv6 | IPv6, no broadcast storms |
| Broadcast | Yes | No, multicast instead | IPv6 |
| NAT | Needed almost everywhere | Not needed | Firewall does all the work |
| DNS record | A | AAAA | Publish both |
| Loopback | 127.0.0.1 |
::1 |
Equal |
| Linux firewall | iptables / nftables ip | ip6tables / nftables ip6 or inet | Rules needed for both |
| Software support | Universal | Very good, a few gaps remain | Run dual stack |
Enterprise Insight:
IPv6-only servers sound clean but they hit real walls. A few well known services still don't publish AAAA records, and GitHub has been the classic example for years, so git clone just fails on a pure IPv6 box. Cloud providers solve this with NAT64 and DNS64 gateways that translate for you. If you go IPv6-only, check that path first with dig AAAA, and our Linux DNS troubleshooting guide helps when name resolution is the part that breaks.
Best Practice:
Don't disable IPv6 just to make it "go away". It hides problems instead of fixing them, and some software (Postfix, parts of systemd, certain Java apps) behaves oddly when the stack is half removed. Instead, keep IPv6 on, make sure every firewall rule exists for both protocols, and bind services only to the addresses they need. Our Linux server hardening checklist has the rest of the lockdown steps.
Red Flags: When IPv6 Quietly Breaks Things
Most IPv6 problems don't throw a clear error, things just get slow or hang, so here are the three symptoms I see most often.
apt update hangs at "0% [Connecting to archive.ubuntu.com (2620:...)]":
Your server has an IPv6 address and a default route, but the path upstream is broken. The system tries IPv6 first, waits for a timeout, then maybe falls back. Community threads are full of this one and the usual "fix" people post is disabling IPv6, which is the wrong fix. Test the path directly with curl -6 -I http://archive.ubuntu.com and ip -6 route show. If v6 really is broken upstream, raise it with the provider, and as a temporary workaround put Acquire::ForceIPv4 "true"; in /etc/apt/apt.conf.d/99force-ipv4. Our curl command guide covers more of these connection tests.
bind() to [::]:8080 failed (98: Address already in use):
On Linux, a socket listening on [::] also accepts IPv4 traffic by default (IPv4-mapped addresses), because net.ipv6.bindv6only is 0. So if your app opens one listener on 0.0.0.0:8080 and another on [::]:8080, the second one fails. Check who holds the port with sudo ss -tlnp | grep 8080 and confirm the setting with sysctl net.ipv6.bindv6only. The fix is usually to listen only on [::], or set the app's ipv6only option. More on reading that output in our ss command guide.
SSH connects over IPv6 but large transfers stall or pages half load:
Classic broken Path MTU Discovery. Someone blocked ICMPv6 at a firewall, so the "Packet Too Big" messages never come back, and since IPv6 routers don't fragment, big packets just vanish. Small packets (handshakes, short commands) still work, which makes it confusing. Run tracepath -6 your-host and look for the pmtu value, and check ip -6 neigh show to confirm NDP is working. On Rocky, make sure you haven't added a rich rule dropping ICMPv6, see our firewall-cmd commands guide for how to list and remove rules.
IPv4 vs IPv6 - FAQ
Q1: Is IPv6 faster than IPv4?
Sometimes, slightly, but not because of the protocol itself. The gain usually comes from skipping carrier-grade NAT, which some mobile and home ISPs use on IPv4. On a clean server-to-server link the difference is basically noise. Test it yourself with curl -4 -o /dev/null -s -w "%{time_total}\n" https://example.com and the same with -6. Measuring like this is a common Linux networking interview question too.
Q2: Should I disable IPv6 on my Linux server?
Usually no. Disabling it is mostly a workaround for a broken config or an incomplete firewall. If you genuinely must (some legacy apps, strict compliance environments), set net.ipv6.conf.all.disable_ipv6 = 1 in a file under /etc/sysctl.d/ and run sudo sysctl --system. Better plan is to firewall it properly, as covered in the hardening checklist.
Q3: Can IPv4 and IPv6 devices talk to each other?
Not directly. They are separate protocols with different headers. Dual stack (running both) is the standard answer, and translation methods like NAT64 exist for IPv6-only networks reaching IPv4 hosts. You can check whether a host supports IPv6 at all with dig AAAA hostname +short, an empty result means IPv4 only. Our DNS troubleshooting guide explains how records and resolution fit in.
Q4: Why does my server have several IPv6 addresses on one interface?
That's normal. You'll see the link-local fe80:: address, any static address you set, and possibly SLAAC or temporary privacy addresses created from Router Advertisements. Run ip -6 addr show and look at the scope and flags, "dynamic" and "temporary" tell you where each came from. On servers you can stop privacy addresses with net.ipv6.conf.all.use_tempaddr = 0. The Linux network administration guide covers interface configuration in more depth.
Q5: How do I write an IPv6 address in a URL or SSH command?
In URLs you wrap it in square brackets, like http://[2001:db8::25]:8080/, because the colons would otherwise clash with the port. SSH accepts the bare address, ssh user@2001:db8::25, but scp and rsync want brackets: scp file user@[2001:db8::25]:/tmp/. For link-local addresses you also need the interface, for example ping fe80::1%ens3. Our Linux networking commands article has more examples of these tools.
Final Thoughts: Run Both, Firewall Both
IPv4 isn't going anywhere soon, and IPv6 is no longer optional. Google crossed 50% of users reaching it over IPv6 for the first time in March 2026, so your visitors are already split. Dual stack is the sane default.
The work is smaller than it sounds. Check what you have with ip -6 addr, make sure every firewall rule covers both protocols, and test each path separately. An hour on one server, and then it's the same steps everywhere.
Next, brush up on the core networking protocols, then go through the server hardening checklist with IPv6 in mind. If you manage a mix of distros, the RHEL vs Ubuntu Server comparison explains why the networking tools differ.
Further Reading on LinuxTeck:
Linux Network Administration Guide - interfaces, routing and the services that sit on top of them.
ss Command in Linux - see exactly which sockets listen on IPv4, IPv6, or both.
Useful firewall-cmd Commands - manage firewalld zones that cover both protocols on Rocky Linux.
Linux DNS Troubleshooting Made Easy - track down A and AAAA resolution problems fast.
Linux Networking Interview Questions - test yourself on IP, routing and troubleshooting.
LinuxTeck - A Complete Linux Infrastructure Blog
LinuxTeck covers everything from beginner Linux commands to advanced Linux system administration and DevOps career guidance, written by practitioners for professionals working on Ubuntu, Rocky Linux, RHEL, and enterprise Linux environments every day.
