Guides

How to disable IPv6 on Ubuntu and Linux (sysctl, GRUB, netplan)

Disable IPv6 on Ubuntu or any Linux with sysctl or the ipv6.disable=1 kernel parameter, per interface or in netplan. Verify it, undo it, or prefer IPv4.

Published ~9 min read
On this page

Short answer: to disable IPv6 on Ubuntu or any Linux until the next reboot, run sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1 and the same for default and lo. To keep it off, put those three settings in a file under /etc/sysctl.d/. To remove IPv6 from the kernel entirely, boot with ipv6.disable=1. ip -6 addr then prints nothing.

Ubuntu · Debian · any Linux with systemd
# off now, until the next reboot
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1

# off for good
printf '%s\n' \
  'net.ipv6.conf.all.disable_ipv6 = 1' \
  'net.ipv6.conf.default.disable_ipv6 = 1' \
  'net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee /etc/sysctl.d/99-disable-ipv6.conf
sudo sysctl --system

# check: no output means no IPv6 addresses left
ip -6 addr

The settings and their effects below follow the Linux kernel documentation (the IPv6 sysctl and module parameter pages), the netplan reference and the Debian 12 man pages for apt.conf and gai.conf. Nothing here was switched off on a test machine for this guide. Before you disable IPv6, check the next section: the usual trigger is one broken thing, and it often has a smaller fix.

Do you really need to disable IPv6?

Disabling IPv6 is a blunt fix for a handful of specific problems. Find yours first:

SymptomActual causeSmaller fix
apt hangs at 0% [Connecting to ... (an IPv6 address)]The server has an IPv6 address but no working IPv6 route outMake apt use IPv4
wget, ssh or pip hang for a long time, then workDNS returns an AAAA record, the IPv6 attempt times out, then IPv4 succeedsPrefer IPv4 in gai.conf
Mail bounces mentioning IPv6 or a missing PTRPostfix sends over IPv6 from an address without reverse DNSinet_protocols = ipv4 in Postfix
A service listens on [::]:22 and looks exposedIt is a dual-stack listener: one socket for IPv4 and IPv6Firewall both protocols instead
connect: Network is unreachableNo IPv6 default route; programs fall back to IPv4Usually nothing: harmless noise

Three commands tell you whether IPv6 on the machine is working, broken or simply absent:

any Linux
ip -6 addr show scope global      # a global address? (2xxx: or 3xxx:)
ip -6 route show default          # a default route out?
ping -6 -c 3 2001:4860:4860::8888 # does it reach anything?

A global address plus a default route plus replies means IPv6 works, and disabling it fixes nothing. A global address without a route, or a route that never answers, is the broken case: fix the address and gateway in the network configuration (the static IP guide shows the netplan file), or disable IPv6 if you have no use for it. Slow name lookups that are not about IPv6 at all are covered in DNS server not responding.

Prefer IPv4 instead of disabling IPv6

If only some programs suffer, make them use IPv4 and leave the stack alone. For APT, apt.conf(5) documents Acquire::ForceIPv4: “When downloading, force to use only the IPv4 protocol.”

Debian · Ubuntu
# once
sudo apt-get -o Acquire::ForceIPv4=true update

# always
echo 'Acquire::ForceIPv4 "true";' | sudo tee /etc/apt/apt.conf.d/99force-ipv4

For everything that resolves names through glibc (curl, wget, git, ssh and most daemons), /etc/gai.conf decides whether IPv6 or IPv4 answers are tried first. Debian and Ubuntu ship the file with the right line already written and commented out, under “For sites which prefer IPv4 connections change the last line to”. Uncomment it:

/etc/gai.conf
precedence ::ffff:0:0/96  100

gai.conf(5) notes that a single precedence line replaces the whole default table, which is what you want here: IPv4-mapped addresses get the highest precedence, so IPv4 is tried first everywhere, while IPv6 still works for anything that asks for it explicitly. Programs pick it up on their next lookup; long-running daemons after a restart. Per command, curl -4, wget -4, ssh -4 and ping -4 do the same for one call.

Method 1: sysctl (all interfaces, no reboot)

disable_ipv6 is a per-interface switch under /proc/sys/net/ipv6/conf/. Per the kernel’s IP sysctl documentation, setting it to 1 deletes all IPv6 addresses and routes on that interface, and no new ones can be added while it stays at 1. The three names matter:

  • all: changing it changes the default value and every existing interface at once.
  • default: the value an interface gets when it is created later (a VPN tunnel, a Docker bridge, a hot-plugged NIC).
  • lo: the loopback interface, which holds ::1. Listing it explicitly makes sure ::1 is gone as well.

Use a file in /etc/sysctl.d/ rather than editing /etc/sysctl.conf: it is read at boot by systemd-sysctl, survives package upgrades, and you can delete it to undo the change. sudo sysctl --system applies every file now and prints each setting as it goes.

Method 2: the kernel parameter ipv6.disable=1

The kernel’s IPv6 module parameters offer a harder switch. With ipv6.disable=1 on the kernel command line the IPv6 code is loaded but all its functionality is off: no addresses, no routes, and no IPv6 sockets at all. The kernel documentation adds that a reboot is required to enable IPv6 again.

Debian · Ubuntu (GRUB)
sudo nano /etc/default/grub
# add the parameter inside the quotes, keeping what is already there:
GRUB_CMDLINE_LINUX="ipv6.disable=1"

sudo update-grub
sudo reboot
Rocky Linux · AlmaLinux · RHEL
sudo grubby --update-kernel=ALL --args="ipv6.disable=1"
sudo reboot
after the reboot
cat /proc/cmdline            # should contain ipv6.disable=1
ls /proc/sys/net/ipv6        # No such file or directory: the IPv6 tree is gone

The difference to the sysctl method shows up in programs that open IPv6 sockets. With sysctl, the socket type still exists and only the addresses are gone; with ipv6.disable=1, creating any IPv6 socket fails with Address family not supported by protocol. An nginx configuration with listen [::]:80; then stops nginx from starting until you remove those lines. Pick the kernel parameter only when you want IPv6 gone for good and are ready to clean up such configuration.

Method 3: one interface only

Sometimes IPv6 should stay on the main interface and disappear from one other: a second NIC, a VPN interface, a bridge. Use the interface name in place of all. ip -br link lists the names (eth0, ens3, enp1s0 and so on, depending on the system):

any Linux
ip -br link
sudo sysctl -w net.ipv6.conf.eth1.disable_ipv6=1
echo 'net.ipv6.conf.eth1.disable_ipv6 = 1' | sudo tee /etc/sysctl.d/99-ipv6-eth1.conf

A name-specific line only applies if the interface already exists when systemd-sysctl runs at boot. For interfaces that appear later, configure IPv6 off in whatever creates them (the VPN client, Docker, netplan).

Ubuntu: turn IPv6 off in netplan

Netplan has no single “IPv6 off” key, but three settings in the interface block stop it from configuring any IPv6 address. From the netplan reference: link-local chooses which link-local addresses to bring up (only IPv6 when the key is absent), accept-ra controls whether router advertisements may configure addresses, and dhcp6 is off by default. An empty link-local list turns off both kinds; [ ipv4 ] would instead make networkd add a 169.254.x.x address. Remove any IPv6 entries from addresses and any IPv6 route as well:

/etc/netplan/50-cloud-init.yaml (your file name may differ)
network:
  version: 2
  ethernets:
    eth0:
      addresses:
        - 203.0.113.10/24
      routes:
        - to: default
          via: 203.0.113.1
      nameservers:
        addresses: [1.1.1.1, 8.8.8.8]
      link-local: [ ]        # no link-local addresses, IPv6 or IPv4
      accept-ra: false
      dhcp6: false
apply with an automatic rollback
sudo netplan try

Use netplan try on a remote server, not netplan apply. Its man page describes it as applying the configuration and rolling it back automatically if you do not confirm within a time limit, 120 seconds by default, which is exactly what you need when a typo would otherwise cut your SSH session. Netplan alone leaves the kernel’s IPv6 stack running (::1 on loopback stays), so combine it with the sysctl file if you want IPv6 fully off.

Side effects, and how to fix them

ServiceWhat you seeFix
Postfixfatal: parameter inet_interfaces: no local interface found for ::1sudo postconf -e inet_protocols=ipv4, then restart Postfix
OpenSSH serverX11 forwarding fails, or sshd logs errors binding to an IPv6 addressAddressFamily inet in /etc/ssh/sshd_config, then sudo sshd -t and restart
nginx, ApacheFails to start because of listen [::]:80 (kernel parameter method)Remove the [::] listen lines, test with nginx -t
Programs that bind to ::1Cannot assign requested addressBind to 127.0.0.1 instead of ::1 or localhost
ufwIPv6 rules fail to load after ipv6.disable=1IPV6=no in /etc/default/ufw, then reload ufw

Leave the ::1 lines in /etc/hosts alone: localhost still resolves to 127.0.0.1 as well, and a program that binds or connects to localhost is better fixed with an explicit 127.0.0.1 (as in the table) than by editing the hosts file. If you keep IPv6 instead, make sure the firewall covers it: the ufw guide sets IPv4 and IPv6 rules together.

Check that IPv6 is really off

any Linux
ip -6 addr                                   # no output
sysctl net.ipv6.conf.all.disable_ipv6        # net.ipv6.conf.all.disable_ipv6 = 1
ss -tlnp | grep '\['                         # no [::] listeners left

With the kernel parameter, the sysctl line answers cannot stat /proc/sys/net/ipv6/conf/all/disable_ipv6: No such file or directory, which also confirms it. To see the addresses that remain on IPv4, checking the IP address on Linux covers ip addr and hostname -I.

How to enable IPv6 again

sysctl method
sudo rm /etc/sysctl.d/99-disable-ipv6.conf
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=0
sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0
sudo netplan apply      # Ubuntu: puts static IPv6 addresses and routes back

Switching back to 0 makes the kernel create a fresh link-local address on each interface, but the addresses and routes it deleted earlier do not return on their own, hence the netplan apply after you have put the IPv6 lines back into the netplan file (or a restart of whichever network service you use, or a reboot). For the kernel parameter, remove ipv6.disable=1 from /etc/default/grub, run sudo update-grub (or sudo grubby --update-kernel=ALL --remove-args="ipv6.disable=1") and reboot.

IPv6 on a VPS: disable it or set it up?

On a server that only ever talks IPv4, disabling IPv6 is a reasonable choice and costs nothing. If the server has a working IPv6 address, though, keeping it lets visitors on IPv6-preferring mobile and home networks connect natively; the IPv4 vs IPv6 comparison weighs that up. A VMHeaven Linux VPS comes with its own IPv4 address, plus an IPv6 prefix when the location’s pool has one free, so you can choose either way. Whatever you change, change network settings with a way back in: netplan try for the configuration files, and an open console session where your provider offers one.

Quick checklist

  • Find the actual problem first: no route, slow AAAA lookups, or mail over IPv6.
  • Prefer IPv4 (apt, gai.conf) when only some programs suffer.
  • Use the sysctl file for a reversible switch, the kernel parameter for a permanent one.
  • Remove IPv6 from netplan too, or networkd may bring it back.
  • Fix Postfix, sshd and [::] listeners before the next restart surprises you.

Frequently asked

How do I disable IPv6 on Ubuntu permanently?

Create /etc/sysctl.d/99-disable-ipv6.conf with the lines net.ipv6.conf.all.disable_ipv6 = 1, net.ipv6.conf.default.disable_ipv6 = 1 and net.ipv6.conf.lo.disable_ipv6 = 1, then run sudo sysctl --system. On Ubuntu Server also set link-local: [ ] and accept-ra: false in the netplan file so networkd does not bring IPv6 back. For a kernel-level switch, add ipv6.disable=1 to GRUB_CMDLINE_LINUX in /etc/default/grub, run sudo update-grub and reboot.

What is the difference between disable_ipv6 and ipv6.disable=1?

The sysctl disable_ipv6 removes IPv6 addresses and routes from interfaces but keeps the IPv6 code in the kernel, and you can switch it back on without a reboot. The kernel parameter ipv6.disable=1 turns off all IPv6 functionality at boot: no addresses and no IPv6 sockets, so programs that try to open one fail with Address family not supported by protocol. Undoing it needs a reboot.

Can I make apt use IPv4 without disabling IPv6?

Yes. Run sudo apt-get -o Acquire::ForceIPv4=true update once, or put the line Acquire::ForceIPv4 "true"; into a file under /etc/apt/apt.conf.d/ to make it permanent. To prefer IPv4 for every program instead, uncomment precedence ::ffff:0:0/96 100 in /etc/gai.conf.

How do I check that IPv6 is disabled?

Run ip -6 addr: no output means no interface has an IPv6 address left. sysctl net.ipv6.conf.all.disable_ipv6 should print 1. With the kernel parameter, /proc/sys/net/ipv6 does not exist at all and cat /proc/cmdline shows ipv6.disable=1.

Related articles

All guides