Why Disable IPv6 in a Homelab
Most home networks and homelabs run exclusively on IPv4. Your router, your DHCP, your firewall rules, your DNS — all IPv4. But every modern Linux install enables IPv6 by default.
This creates a problem: IPv6 traffic exists on your network but your firewall rules probably don't cover it. UFW has separate rulesets for IPv4 and IPv6, and most people only configure IPv4 rules. iptables and ip6tables are entirely separate tools. If you only configured iptables, IPv6 traffic flows unfiltered.
Even on a LAN-only network, IPv6 link-local addresses are automatically assigned and can be used for communication between hosts. This is traffic your monitoring and firewall may not be watching.
The safe choice: if you're not actively using IPv6, disable it. You can always re-enable it later when you're ready to set up proper IPv6 firewalling.
Step 1: Check Your Current IPv6 Status
See what IPv6 addresses are currently assigned to your interfaces:
ip -6 addr showInterpret the Output
You'll see output like:
1: lo: <LOOPBACK,UP>
inet6 ::1/128 scope host
2: eth0: <BROADCAST,MULTICAST,UP>
inet6 fe80::a00:27ff:fe5a:1234/64 scope link
- ::1 is the IPv6 loopback (like 127.0.0.1)
- fe80:: addresses are link-local (auto-assigned, used for neighbor discovery)
- If you see addresses starting with 2xxx: or fd:: those are real routable IPv6 addresses — you might actually be using IPv6. Investigate before disabling.
Also check if any services are listening on IPv6:
sudo ss -tulnp | grep '\[::\]'What the Listening Output Means
If you see services bound to [::]:PORT (like [::]:22 for SSH), those services are accepting connections on both IPv4 and IPv6. After disabling IPv6, they'll fall back to IPv4 only — which is what we want.
If you see services ONLY on IPv6 (rare in homelabs), disabling IPv6 will break them. Check for this before proceeding.
Step 2: Disable IPv6 via sysctl
Create a sysctl configuration file that disables IPv6 on all interfaces. This is the standard, recommended method:
sudo tee /etc/sysctl.d/99-disable-ipv6.conf << 'EOF'
# Disable IPv6 on all interfaces
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
EOFStep 2b: Apply Immediately
Load the new settings without rebooting:
sudo sysctl --system 2>&1 | grep ipv6Validate: What You Should See
You should see lines confirming each setting was applied:
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
If you see "error: permission denied" — make sure you're using sudo.
If you see "error: No such file or directory" — your kernel might not have IPv6 compiled in (unlikely but possible in some minimal VMs).
Step 3: Verify IPv6 Is Disabled
Check that no IPv6 addresses remain on your interfaces:
ip -6 addr showExpected Output
You should see either NO output or only the loopback entry:
1: lo: <LOOPBACK,UP>
inet6 ::1/128 scope host
The loopback ::1 may persist on some systems even after disabling. That's usually harmless.
If you still see fe80:: addresses on your network interfaces, the sysctl didn't take effect. Check:
sysctl net.ipv6.conf.all.disable_ipv6
This should return 1. If it returns 0, the setting was overridden. Check for conflicting files:
grep -r 'disable_ipv6' /etc/sysctl.d/ /etc/sysctl.conf
Step 4: Verify Services Still Work
After disabling IPv6, check that your services are still running and accessible:
sudo ss -tulnp | head -20What to Look For
Services that were listening on [::]:PORT should now show 0.0.0.0:PORT (IPv4 only). Some services may need a restart to rebind:
sudo systemctl restart sshd
sudo systemctl restart docker
If any service fails to start after disabling IPv6, check its config for hardcoded IPv6 addresses (like ::1 or [::]) and change them to IPv4 equivalents (127.0.0.1 or 0.0.0.0).
Step 5: Handle Docker
Docker may re-enable IPv6 on its bridge networks. Explicitly disable it in the Docker daemon config:
Check if the config file exists:
cat /etc/docker/daemon.json 2>/dev/null || echo '{}'Step 5b: Update Docker Config
If the file doesn't exist or is empty, create it. If it exists with other settings, add the ip6tables and ipv6 keys to the existing JSON:
For a new/empty config:
echo '{
"ipv6": false,
"ip6tables": false
}' | sudo tee /etc/docker/daemon.jsonStep 5c: Restart Docker
Apply the Docker config change. WARNING: This restarts all running containers:
sudo systemctl restart dockerStep 5d: Verify Docker Containers Are Running
Make sure your containers came back up:
docker ps --format 'table {{.Names}}\t{{.Status}}' | head -20Step 6: Verify Persistence Across Reboot
The sysctl.d file should persist across reboots. To confirm without actually rebooting, check that the file exists and contains the right values:
cat /etc/sysctl.d/99-disable-ipv6.conf && echo '---' && sysctl net.ipv6.conf.all.disable_ipv6For Extra Persistence: GRUB (Optional)
On some systems (particularly older kernels), sysctl alone may not disable IPv6 early enough in the boot process. You can also disable it via the kernel command line in GRUB:
sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1 /' /etc/default/grub
sudo update-grub
This is belt-and-suspenders — the sysctl method is sufficient on Ubuntu 22.04+ and Debian 12+. Only add the GRUB method if you find IPv6 re-enabling after reboots.
After modifying GRUB, you need to reboot for the kernel parameter to take effect.
How to Undo Everything
To re-enable IPv6:
1. Remove the sysctl config:
sudo rm /etc/sysctl.d/99-disable-ipv6.conf
sudo sysctl --system
2. If you modified GRUB:
sudo sed -i 's/ipv6.disable=1 //' /etc/default/grub
sudo update-grub
# Reboot required
3. If you modified Docker:
# Remove the ipv6 and ip6tables lines from /etc/docker/daemon.json
sudo systemctl restart docker
4. Verify IPv6 is back:
ip -6 addr show
# You should see fe80:: addresses on your interfaces again