Static IPv4 and IPv6 configuration on Debian with /etc/network/interfaces
Networking

Debian Static IP and IPv6 with /etc/network/interfaces

Most of the time a fresh VPS boots with its network already working, and nobody looks at how. Then the day comes when you order a second IP for a new site, want to switch on the IPv6 block that came with the server, or need to move the machine to a new address, and suddenly you are editing the one file that can cut you off from your own server. One typo in /etc/network/interfaces, one wrong gateway, and your SSH session goes quiet forever.

In this guide we set up a Debian static IP the classic way, with ifupdown and /etc/network/interfaces: the main IPv4 address, IPv6, extra addresses and DNS resolvers. Just as important, we apply the changes with a safety net, so that a mistake costs you ten minutes instead of a support ticket, and we verify the result properly with ip and ping instead of hoping for the best.

What ifupdown and /etc/network/interfaces actually are

Before touching anything, a few terms we will use all the time:

  • Network interface: the (virtual) network card of your server. On a VPS it usually has a name like ens18, ens3, eth0 or enp1s0. There is also lo, the loopback interface, which only talks to the server itself.
  • IP address and prefix: 203.0.113.10/24 means the address 203.0.113.10 in a network whose first 24 bits are fixed, so every address from 203.0.113.0 to 203.0.113.255 is “local”. The /24 is the CIDR prefix, the modern way of writing a netmask (/24 is the same as 255.255.255.0).
  • Gateway: the router your server sends traffic to when the destination is not in the local network, in other words, the door to the Internet. IPv4 and IPv6 each have their own gateway.
  • DNS resolver: the server that turns names like deb.debian.org into IP addresses. Without one, ping 1.1.1.1 works but apt update fails.

ifupdown is Debian’s traditional network configuration tool. At boot, the networking.service unit runs ifup -a, which reads /etc/network/interfaces and turns every stanza (block of lines starting with iface) into ip addr add and ip route add commands for the kernel. You can also run ifup, ifdown and ifquery by hand. The format is plain text, has existed for decades and is documented in man interfaces.

Why a static address instead of DHCP? On a server, the IP is a promise: your DNS records point to it, firewall rules and allow-lists mention it, reverse DNS is set for it. Writing it down explicitly removes the dependency on a DHCP server answering at every boot and makes the configuration readable at a glance.

How the config file becomes a working networkConfig file/etc/network/interfacesifupdownifup / ifdownnetworkingens18 (VPS NIC)203.0.113.10/24 main198.51.100.7/32 extra2001:db8:10::10/642001:db8:10::20/64 extraGateway203.0.113.12001:db8:10::1InternetIPv4 + IPv6/etc/resolv.confnameserver linesDNS resolvers1.1.1.1 / 9.9.9.9 / IPv6IPv4 addressIPv6 addressExample addresses,replace with yours
ifupdown reads /etc/network/interfaces and configures the interface; traffic leaves through the gateway, names are resolved via the servers in /etc/resolv.conf.

Debian, Ubuntu and the other network managers

Linux has several tools that can own the network, and they must not fight over the same interface:

  • Debian 13 (and 12) installed as a server uses ifupdown and /etc/network/interfaces. This guide is written for that case.
  • Ubuntu 24.04 uses netplan, which generates configuration for systemd-networkd. /etc/network/interfaces is absent or ignored there. We give a short netplan equivalent near the end.
  • Some Debian cloud images use netplan, systemd-networkd or cloud-init generated files instead of a hand-written interfaces file, and desktops use NetworkManager.

The rule is simple: one manager per interface. Step 2 below shows how to check which one your server uses.

A note on LXC containers

If your VPS is an LXC container rather than a KVM virtual machine, the network is usually defined on the host side. On many LXC platforms, including Proxmox-based ones, the host rewrites the container’s network file every time the container starts, so your edits disappear after a restart. In that case, change IPs from the control panel or ask support, and use this guide mainly for the verification part. On KVM, the network inside the VM is entirely yours.

What you need

  • A Debian 13 VPS (Debian 12 works the same) that uses ifupdown, and a user with sudo.
  • Your network details from the provider: main IPv4 address and prefix, IPv4 gateway, IPv6 address or block and its gateway, any extra IPs, and preferred DNS resolvers. You find them in the client area or the welcome email.
  • Out-of-band console access, such as the VNC/noVNC console in your VPS control panel. Open it now and confirm you can log in with a password. If the network breaks, this is your way back in.
  • About 20 minutes and, for a production server, a quiet moment in case a reboot is needed.

All addresses in this article come from the ranges reserved for documentation (203.0.113.0/24, 198.51.100.0/24, 2001:db8::/32). Replace them with your real ones.

Configuring a Debian static IP in /etc/network/interfaces, step by step

Here is the whole procedure at a glance. Steps 1 to 3 change nothing, steps 4 to 7 only edit files, and only step 8 actually touches the live network, with a rollback armed.

The 9 steps in this guide1Gathercurrent details2Confirmifupdown in charge3Back upthe config4StaticIPv45StaticIPv66ExtraIP addresses7DNSresolvers8Arm rollbackand apply9Verifyand disarmPrepare (1-3)Edit files (4-7)Apply and verify (8-9)
Steps 1-3 are read-only preparation, steps 4-7 edit files, step 8 applies the change with a rollback armed and step 9 verifies it and disarms the rollback.

Step 1: Gather your current network details

First we look at what the server is doing right now. Nothing here changes anything:

ip -br link
ip -br addr
ip route
ip -6 route
cat /etc/resolv.conf

ip -br prints a brief, one line per interface view. On a typical VPS you see something like this:

lo               UNKNOWN        127.0.0.1/8 ::1/128
ens18            UP             203.0.113.10/24 fe80::be24:11ff:fe2a:3b4c/64
default via 203.0.113.1 dev ens18
203.0.113.0/24 dev ens18 proto kernel scope link src 203.0.113.10

Write down three things: the interface name (ens18 here, yours may differ), the current address with prefix and the default gateway (the default via line). The fe80:: address is an IPv6 link-local address that every interface creates automatically; it is not your public IPv6. If the route shows proto dhcp, the server currently gets its address from DHCP.

Step 2: Confirm that ifupdown is in charge

Now we make sure we are editing the file that actually matters:

systemctl is-active networking systemd-networkd NetworkManager
ls /etc/netplan/ 2>/dev/null
ls /etc/network/interfaces.d/

On a Debian server managed by ifupdown, the first command prints active, inactive, inactive (one line per service; a service that is not installed also shows inactive). The /etc/netplan/ listing should be empty or missing. If systemd-networkd or NetworkManager is active, or netplan has YAML files, your server is not using ifupdown for this interface: stop here and configure that tool instead, do not mix them.

Check /etc/network/interfaces.d/ too. If you find a file like 50-cloud-init, or the main file starts with a comment saying it was generated by cloud-init, cloud-init will regenerate it. Tell cloud-init to leave the network alone, then move the content you need into /etc/network/interfaces:

echo 'network: {config: disabled}' | sudo tee /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg

If dpkg -s ifupdown2 reports the package as installed, you have ifupdown2, a rewrite with the same file format. Everything below applies, and you get the extra ifreload -a command in step 8.

Step 3: Back up the current configuration

A known-good copy of the file is what the rollback in step 8 will restore, so we make it now, while the network is working:

sudo cp /etc/network/interfaces /root/interfaces.good
sudo cp /etc/network/interfaces "/root/interfaces.$(date +%F-%H%M)"
cat /etc/network/interfaces

The first copy has a fixed name the rollback can find; the second is a dated archive. A freshly installed Debian often has a file like this:

source /etc/network/interfaces.d/*

auto lo
iface lo inet loopback

allow-hotplug ens18
iface ens18 inet dhcp

Step 4: Write the static IPv4 configuration

Open the file with an editor (nano is installed by default):

sudo nano /etc/network/interfaces

Replace the interface part with a static stanza. Keep the source and loopback lines as they are:

source /etc/network/interfaces.d/*

# Loopback
auto lo
iface lo inet loopback

# Main IPv4
auto ens18
iface ens18 inet static
    address 203.0.113.10/24
    gateway 203.0.113.1

Line by line:

  • auto ens18 brings the interface up at boot through networking.service. The installer often writes allow-hotplug, which waits for the kernel to announce the device; on a server, auto is the more predictable choice because boot waits for it before reaching “network online”.
  • iface ens18 inet static starts an IPv4 (inet) stanza with a fixed address.
  • address takes the address with its CIDR prefix. The older two-line style, address 203.0.113.10 plus netmask 255.255.255.0, still works, but the CIDR form is shorter and harder to get wrong.
  • gateway sets the default route. Write only one IPv4 gateway in the whole file.

Indentation is optional but makes the file readable, and anything after # is a comment. If you are changing the main IP rather than just switching from DHCP, also check /etc/hosts: Debian maps the hostname to 127.0.1.1, which is fine, but some images list the public IP, which you then need to update.

Step 5: Add IPv6

IPv6 gets its own stanza for the same interface, with inet6 instead of inet. No second auto line is needed, auto ens18 already covers every stanza of ens18:

# Main IPv6
iface ens18 inet6 static
    address 2001:db8:10::10/64
    gateway 2001:db8:10::1
    accept_ra 0
    autoconf 0

If your provider assigned you a whole /64 (or larger), you choose the host part yourself: ::10 is just an easy to remember example. Do not pick the address the gateway uses, often ::1. Some providers give the gateway as a link-local address such as fe80::1; that works perfectly in the gateway line, because the route is tied to ens18.

accept_ra 0 tells the kernel to ignore router advertisements, and autoconf 0 stops it from inventing extra addresses from them (SLAAC). On a server with a static setup we want exactly the addresses and the gateway we wrote, nothing more; we set both explicitly so the result does not depend on defaults. If your provider tells you IPv6 is delivered only via SLAAC, use iface ens18 inet6 auto instead and leave out the address and gateway.

Step 6: Add extra IP addresses

Extra addresses on the same interface are simply more stanzas, without a gateway line. Two common cases:

  • An extra IPv4 in the same subnet as the main one: use the same prefix, for example 203.0.113.11/24.
  • An extra IPv4 routed to your server from another range (very common for additional IPs): use /32, meaning “just this one address”.
# Extra IPv4 routed to this server
iface ens18 inet static
    address 198.51.100.7/32

# Extra IPv6 from the same /64
iface ens18 inet6 static
    address 2001:db8:10::20/64

You may see older guides using alias interfaces like ens18:0. They still work for IPv4, but the alias labels confuse some tools and do not exist for IPv6; multiple stanzas are the cleaner way. Outgoing connections still use the main address as source, and services such as a web server choose which address they listen on.

Before applying anything, you can test that the provider actually routes the extra address to you, live and without touching the file:

sudo ip addr add 198.51.100.7/32 dev ens18
ping -c 3 -I 198.51.100.7 1.1.1.1
sudo ip addr del 198.51.100.7/32 dev ens18

Replies mean the address works. We remove the test address at the end so ifupdown can add it cleanly in step 8; if it already exists, ifup fails with RTNETLINK answers: File exists.

Step 7: Configure DNS resolvers

How DNS is configured depends on who manages /etc/resolv.conf. Check it first:

ls -l /etc/resolv.conf
  • A regular file (the usual case on a Debian 13 server): edit it directly with sudo nano /etc/resolv.conf. At most three nameserver lines are used. Prefer your provider’s resolvers if they gave you any; otherwise public ones work:
nameserver 1.1.1.1
nameserver 9.9.9.9
nameserver 2606:4700:4700::1111
  • A symlink to ../run/resolvconf/resolv.conf: the resolvconf package is installed, and you add dns-nameservers 1.1.1.1 9.9.9.9 (and optionally dns-search example.com) inside the main inet static stanza. Without resolvconf, these dns- lines are silently ignored, which surprises a lot of people.
  • A symlink to ../run/systemd/resolve/stub-resolv.conf: systemd-resolved is in charge (the default on Ubuntu). Set DNS= in /etc/systemd/resolved.conf, run sudo systemctl restart systemd-resolved and check with resolvectl status.

The complete file now looks like this:

source /etc/network/interfaces.d/*

# Loopback
auto lo
iface lo inet loopback

# Main IPv4
auto ens18
iface ens18 inet static
    address 203.0.113.10/24
    gateway 203.0.113.1

# Main IPv6
iface ens18 inet6 static
    address 2001:db8:10::10/64
    gateway 2001:db8:10::1
    accept_ra 0
    autoconf 0

# Extra IPv4 routed to this server
iface ens18 inet static
    address 198.51.100.7/32

# Extra IPv6 from the same /64
iface ens18 inet6 static
    address 2001:db8:10::20/64

Step 8: Check the syntax, arm a rollback and apply

First a dry run. --no-act prints what ifup would do without doing it, and --force makes it ignore the fact that ens18 is already up:

sudo ifup --no-act --force ens18

You should see a list of ip addr add and ip route add commands for every stanza. A typo produces an error naming the problem and line instead, so fix it and run the check again.

Next, the safety net. We create a small systemd timer that, ten minutes from now, restores /root/interfaces.good and reboots. Because it is a real unit file and not a temporary one, it also survives a reboot: if the server boots with a broken network, the timer fires ten minutes after boot. The service disables itself first, so it can never loop:

sudo tee /etc/systemd/system/net-rollback.service >/dev/null <<'EOF'
[Unit]
Description=Restore the previous network configuration and reboot

[Service]
Type=oneshot
ExecStart=/bin/sh -c 'systemctl disable net-rollback.timer; cp /root/interfaces.good /etc/network/interfaces; systemctl reboot'
EOF

sudo tee /etc/systemd/system/net-rollback.timer >/dev/null <<'EOF'
[Unit]
Description=Network rollback safety net

[Timer]
OnActiveSec=10min

[Install]
WantedBy=timers.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now net-rollback.timer
systemctl list-timers net-rollback.timer

The last command shows the timer with roughly 10 minutes left. Now apply the configuration, choosing one of two ways:

Option A, reboot (recommended when you change the main IP or gateway, or switch from DHCP to static):

sudo systemctl reboot

Option B, restart networking (fine when the main address stays the same and you only add IPv6 or extra IPs):

sudo systemctl restart networking

The restart runs inside systemd, not in your shell, so it finishes even if your SSH session freezes for a moment. Its limitation: ifdown removes what the file describes now, so an address from the old version of the file can stay on the interface until the next reboot (or until you delete it with ip addr del). That is exactly why a reboot is the cleaner test when the main address changes. With ifupdown2, sudo ifreload -a applies only the differences.

If you changed the main IP, reconnect to the new address: ssh youruser@203.0.113.10.

Approximate SSH interruption by apply methodApply methodip addr add (live)not persistent~0 srestart networkingtests the file~2-5 sReboot (small VPS)tests full boot~20-40 s010203040Seconds of SSH interruption (approximate)
Step 8: approximate interruption per apply method on a small KVM VPS. The longer the interruption, the more of the real boot path you test.

Step 9: Verify routing with ip and ping, then disarm the rollback

Once you are back in, check the addresses and routes the kernel actually has:

ip -br addr show dev ens18
ip route
ip -6 route

Expected, roughly:

ens18            UP             203.0.113.10/24 198.51.100.7/32 2001:db8:10::10/64 2001:db8:10::20/64 fe80::be24:11ff:fe2a:3b4c/64
default via 203.0.113.1 dev ens18
203.0.113.0/24 dev ens18 proto kernel scope link src 203.0.113.10
2001:db8:10::/64 dev ens18 proto kernel metric 256 pref medium
fe80::/64 dev ens18 proto kernel metric 256 pref medium
default via 2001:db8:10::1 dev ens18 metric 1024 pref medium

Every address from the file must be present, with exactly one IPv4 and one IPv6 default route. Then ask the kernel which path and source address it would pick for a destination:

ip route get 1.1.1.1
ip -6 route get 2606:4700:4700::1111

The output (1.1.1.1 via 203.0.113.1 dev ens18 src 203.0.113.10 ...) confirms the gateway and source address. Now real traffic, the gateway first, then the Internet, then each extra address as source, then DNS:

ping -c 3 203.0.113.1
ping -c 3 1.1.1.1
ping -6 -c 3 2606:4700:4700::1111
ping -c 3 -I 198.51.100.7 1.1.1.1
ping -6 -c 3 -I 2001:db8:10::20 2606:4700:4700::1111
getent ahosts deb.debian.org

Each ping should report 3 received, 0% packet loss. Some gateways ignore ping, so a silent gateway is fine as long as the Internet pings work. getent should print IPv4 and IPv6 addresses for the name, proving DNS works. If ping is missing on a minimal install, add it with sudo apt install iputils-ping. Finally, test from outside: from your own computer, ping 203.0.113.10 and, if your connection has IPv6, ping -6 2001:db8:10::10 or ssh -6 youruser@2001:db8:10::10.

Everything works? Disarm the rollback before it fires:

sudo systemctl disable --now net-rollback.timer
systemctl list-timers net-rollback.timer

The second command should say 0 timers listed. Keep the two unit files for next time, and once the new file has proved itself, copy it over /root/interfaces.good so the next rollback returns to this version. If you used option B, plan a normal reboot at a quiet moment to confirm the configuration also comes up from a cold start.

What about Ubuntu 24.04?

Ubuntu does not use /etc/network/interfaces, and installing ifupdown next to netplan only creates conflicts. The same configuration in netplan goes in the existing YAML file under /etc/netplan/ (often 50-cloud-init.yaml; if cloud-init generated it, disable its network config as in step 2):

network:
  version: 2
  ethernets:
    ens18:
      dhcp4: false
      dhcp6: false
      accept-ra: false
      addresses:
        - 203.0.113.10/24
        - 198.51.100.7/32
        - 2001:db8:10::10/64
        - 2001:db8:10::20/64
      routes:
        - to: default
          via: 203.0.113.1
        - to: default
          via: 2001:db8:10::1
      nameservers:
        addresses: [1.1.1.1, 2606:4700:4700::1111]

Make the file readable only by root (sudo chmod 600 /etc/netplan/*.yaml) and apply it with sudo netplan try, which has a built-in rollback: if you do not confirm within 120 seconds, the old configuration returns. On Ubuntu, DNS goes through systemd-resolved, so verify it with resolvectl status. The verification commands from step 9 are identical.

Troubleshooting and common mistakes

  • Wrong interface name. Cannot find device "eth0" means the file names an interface that does not exist. Compare with ip -br link. This is the most common way to lose a server.
  • Two gateway lines in the same family. The second ip route add default fails with RTNETLINK answers: File exists and ifup may leave the interface half configured. Keep one IPv4 and one IPv6 gateway in total.
  • Gateway outside your subnet. Some providers give a /32 with a gateway in another range. The gateway line then fails because the kernel cannot reach it. Drop it and use post-up ip route add 192.0.2.1 dev ens18 followed by post-up ip route add default via 192.0.2.1 dev ens18 in the stanza (with your provider’s gateway).
  • IPv6 address marked tentative or dadfailed. Check ip -6 addr show dev ens18. tentative lasts a second or two while the kernel checks for duplicates; dadfailed means another machine already uses that address, so choose a different one.
  • IPv6 does not answer at all. Check the gateway with ip -6 neigh show dev ens18. If it shows FAILED, the gateway address is wrong or the block is not routed to your server yet: ask your provider.
  • DNS settings ignored or overwritten. dns-nameservers lines need the resolvconf package. If /etc/resolv.conf keeps changing, a leftover DHCP client, cloud-init or systemd-resolved is writing it. A reboot after leaving DHCP stops the old client.
  • ifup: interface ens18 already configured. ifup does not reconfigure an interface that is up. Use systemctl restart networking or a reboot, as in step 8.
  • The old address is still there after a restart. Remove it with sudo ip addr del 203.0.113.99/24 dev ens18 or reboot.
  • Locked out. Wait for the rollback timer to restore the old file and reboot, or log in through the control panel console, run cp /root/interfaces.good /etc/network/interfaces and reboot. journalctl -u networking -b shows what failed.

Security notes

  • Use only addresses assigned to you. Providers filter traffic from addresses that are not yours, and “borrowing” an IP disrupts its owner and breaks the terms of service.
  • Every new address is a new door. Services listening on 0.0.0.0 or :: answer on all addresses, including the IPv6 one you just enabled. Run sudo ss -tulpn and bind services to specific addresses where it makes sense.
  • Firewall IPv6 too. ufw handles IPv6 when IPV6=yes in /etc/default/ufw (the default); with nftables, use inet tables so one rule set covers both families. Remember that ports published by Docker bypass ufw, so do not rely on ufw alone for containers.
  • Keep router advertisements off on static servers, as in step 5, so nothing on the network can add addresses or routes behind your back.
  • Keep the console usable. It is your recovery path: the user you log in with there needs a working password, even if SSH uses keys only.
  • Set reverse DNS for addresses that send mail; it is configured on the provider side, from the control panel or through support.

So, is ifupdown still worth learning?

Absolutely, if you run Debian servers. /etc/network/interfaces is short, readable and boring in the best possible way: a static IPv4, IPv6, extra addresses and DNS fit in twenty lines you can understand a year later. What makes the difference is not the syntax but the routine around it: gather the details, back up, dry run, arm a rollback, apply, verify with ip and ping, and only then disarm. Do that and changing a server’s network stops being scary.

You can practice all of this on a VPS from us, with IPv6 included and a console in the control panel for exactly these moments. And if you would rather have someone else plan IP changes, extra addresses and firewall rules for production machines, our server administration team does this every day.

FAQ

Do I need to reboot after editing /etc/network/interfaces?

Not always. Adding IPv6 or extra addresses works with sudo systemctl restart networking. When the main address or gateway changes, or you move from DHCP to static, a reboot is cleaner and also proves the configuration survives a cold start.

Can I use DHCP for IPv4 and static for IPv6?

Yes. Keep iface ens18 inet dhcp and add an iface ens18 inet6 static stanza below it. Each address family is configured independently.

What is the difference between ifupdown and ifupdown2?

ifupdown2 is a rewrite that reads almost the same file format and adds ifreload -a, which applies only what changed. It is used by Proxmox VE, for example. On a normal Debian VPS, the default ifupdown is perfectly fine and there is no need to switch.

How many extra IP addresses can I add?

Technically dozens or hundreds, one stanza each. The real limit is how many your provider assigns to you. For large blocks, ask for a routed subnet and keep the file tidy with comments.

Trusted By & Member Of

We are proud members of leading internet infrastructure organizations.

RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX