Since the exhaustion of IPv4 addresses, the IPv6 kernel module typically enables and configures IPv6 on network interfaces automatically. However, some scenarios work only when IPv6 is off.
In this tutorial, we explain how to disable IPv6 globally and on a given network interface. First, we look at a Linux pseudo-filesystem and its associated tool, both of which expose kernel settings. Next, we see how they can be leveraged to disable IPv6 for the whole system and for specific interfaces. Finally, we demonstrate how each setting works.
We tested the code in this tutorial on Debian 11 (Bullseye) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments.
2. The /proc Pseudo-Filesystem and the sysctl Tool
Linux provides the /proc procfs pseudo-filesystem, a UNIX-remnant interface to internal kernel configuration:
$ ls --ignore '[0-9]*' /proc acpi diskstats ioports loadavg pressure thread-self asound dma irq locks schedstat timer_list buddyinfo driver kallsyms meminfo self tty bus dynamic_debug kcore misc slabinfo uptime cgroups execdomains keys modules softirqs version cmdline fb key-users mounts stat vmallocinfo consoles filesystems kmsg mtrr swaps vmstat cpuinfo fs kpagecgroup net sys zoneinfo crypto interrupts kpagecount pagetypeinfo sysrq-trigger devices iomem kpageflags partitions sysvipc
Here, we use –ignore ‘[0-9]*’ to exclude objects with numerical names, i.e., IDs of current processes, since such objects are directories that expose data about running instances by process ID (PID).
As we can see, besides them, there are many general categories of information. These are spread in a hierarchical structure of directories and files. Essentially, reading files from /proc extracts configuration parameters, while changing files in /proc modifies settings.
To organize these changes and make them easier to perform, many Linux distributions provide the sysctl utility. Let’s use it to see all available settings:
$ sysctl --all [...] user.max_user_namespaces = 7323 user.max_uts_namespaces = 7323 vm.admin_reserve_kbytes = 8192 vm.compact_unevictable_allowed = 1 vm.compaction_proactiveness = 20 vm.dirty_background_bytes = 0 [...]
The list is long but aims to replicate the structure of /proc. Of course, some settings are read-only.
Now, let’s read and change a setting directly and via sysctl:
$ cat /proc/sys/fs/file-max 9223372036854775807 $ printf 9223372036854775806 > /proc/sys/fs/file-max $ sysctl fs.file-max fs.file-max = 9223372036854775806 $ sysctl fs.file-max=9223372036854775807 fs.file-max = 9223372036854775807
First, we check the setting by displaying the contents of /proc/sys/fs/file-max with cat. Next, we use printf and stream redirection to write a new value in the file. After that, we verify the value of the setting with sysctl by passing its proper breadcrumb path. Lastly, we write a new value with =, which sysctl automatically echoes to confirm.
Finally, to persist settings between reboots, we can write them to /etc/sysctl.conf, again as key=values pairs, one per line. In this article, we’ll only use sysctl since it’s equivalent to direct editing of the files in /proc.
3. Disable IPv6 with sysctl
To disable IPv6, we can use sysctl and its net.ipv6.conf category. Let’s see how.
There are two general settings for disabling IPv6 via sysctl: net.ipv6.conf.all.disable_ipv6 and net.ipv6.conf.default.disable_ipv6. However, turned on by assigning 1 instead of 0, they prevent the system from configuring IPv6 on any network by default or manually:
$ sysctl net.ipv6.conf.all.disable_ipv6=1 $ sysctl net.ipv6.conf.default.disable_ipv6=1
To disable IPv6 on a particular interface, we can use another setting: net.ipv6.conf.IFACE.disable_ipv6, where IFACE is the name of the interface of interest. Importantly, to use IPv6, both the interface-specific and the system-wide disable settings have to be set to 0, while only one set to 1 disables it:
$ sysctl net.ipv6.conf.all.disable_ipv6=1 $ sysctl net.ipv6.conf.default.disable_ipv6=1 $ sysctl net.ipv6.conf.eth0.disable_ipv6=1
Now that we know the settings and their relations, we can verify their consequences.
Let’s see how configuring the kernel network options takes effect. To that end, we’ll poll or attempt to change our interface with ip before and after toggling them. Our target network interface will be eth0.
First, we check the current values of the IPv6 disabling settings:
$ sysctl net.ipv6.conf.all.disable_ipv6 net.ipv6.conf.all.disable_ipv6 = 0 $ sysctl net.ipv6.conf.default.disable_ipv6 net.ipv6.conf.default.disable_ipv6 = 0 $ sysctl net.ipv6.conf.eth0.disable_ipv6 net.ipv6.conf.eth0.disable_ipv6 = 0
We have left IPv6 enabled and allowed globally and on eth0. Next, we check the current network interface settings:
$ ip address show eth0 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:11:06:66:00:10 brd ff:ff:ff:ff:ff:ff inet 192.168.6.66/24 brd 192.168.6.255 scope global noprefixroute eth0 valid_lft forever preferred_lft forever inet6 2001:db8:0:666::1/64 scope global valid_lft forever preferred_lft forever
Indeed, we have an IPv6 address set as 2001:db8:0:666::1/64. After this, we disable IPv6 for the eth0 interface and again poll the network settings:
$ sysctl net.ipv6.conf.eth0.disable_ipv6=1 net.ipv6.conf.eth0.disable_ipv6 = 1 $ ip address show eth0 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:11:06:66:00:10 brd ff:ff:ff:ff:ff:ff inet 192.168.6.66/24 brd 192.168.6.255 scope global noprefixroute eth0 valid_lft forever preferred_lft forever
Now, the inet6 lines have disappeared, as expected. In fact, we can’t set an IPv6 address, even as a superuser:
# ip -6 addr add 2001:0db8:0:0666::1/64 dev eth0 RTNETLINK answers: Permission denied
Hence, we successfully disabled IPv6 for a given interface. Appending net.ipv6.conf.eth0.disable_ipv6=1 to /etc/sysctl.conf or a new file in the /etc/sysctl.d/ directory, we can make the setting permanent for that interface.
5. GRUB IPv6 Disable
While there is a way to turn IPv6 off via GRUB, it’s rarely the recommended one unless we want to save resources.
In essence, it comes down to appending ipv6.disable=1 to the GRUB_CMDLINE_LINUX variable in /etc/default/grub. Next, we only need to update the GRUB settings:
$ grub2-mkconfig -o /boot/grub2/grub.cfg
After a reboot, the IPv6 kernel module will be off, preventing us from changing any settings via sysctl or /proc. Because of this restriction, the method is usually not preferred.
In this article, we looked at the most common way to disable IPv6 on modern Linux distributions.
In conclusion, while IPv6 is required in some cases, it is easy and can be valuable to disable it.