Debian

How To Configure VLAN Interface on Debian 13

A VLAN interface on Debian is a child of a physical NIC that adds an 802.1Q tag to every frame it sends. That lets one cable carry the management network untagged and VLAN 10 tagged together. You do not need the old vlan package or vconfig any more, because the kernel’s 8021q module loads on demand and current ifupdown creates the tagged interface itself. What trips people up is knowing which network manager actually owns the NIC on your box.

Original content from computingforgeeks.com - post 74993

You’ll set up VLAN 10 four ways: a runtime test with ip link, ifupdown in /etc/network/interfaces, systemd-networkd .netdev files, and nmcli. The three persistent methods each survived a reboot, and tcpdump showed the 802.1Q tag on the wire. The steps below were run on Debian 13 in October 2026. Configure the VLAN in whichever tool already owns your NIC, and only add to the parent’s config, never rewrite it.

Prerequisites

A VLAN only works when the other end of the cable speaks it, so the switch side matters as much as the Debian side. The port your server plugs into must be a trunk (Cisco) or a tagged member (most other vendors) for the VLAN IDs you plan to use. You need the following:

  • A Debian 13 server with sudo access and one wired NIC
  • A switch port carrying VLAN 10 tagged, or a hypervisor bridge that passes tagged frames
  • A free address on the VLAN subnet (we use 10.0.10.11/24)
  • Console access (IPMI, hypervisor console) in case a network change cuts your SSH session

Unmanaged switches either drop tagged frames or pass them blindly, so neither gives you VLAN separation. If you’re still buying hardware, a cheap managed gigabit switch with 802.1Q support is enough for a home lab. Our lab was a Debian 13 VM on Proxmox VE whose vmbr0 bridge has VLAN filtering off, which means the bridge forwards tagged frames untouched. If your Proxmox bridge is VLAN aware, the VM’s tagged traffic must be allowed by the bridge’s VLAN list, and the Proxmox Linux bridges guide covers both modes.

Find the NIC name and the network manager

Before writing any config, find out which tool brings the NIC up, because a VLAN defined in the wrong tool’s files is simply ignored. Your server can be running any of three: ifupdown (networking.service), systemd-networkd (netplan drives it on cloud images), or NetworkManager on desktops. The Debian 13 cloud image we started from had no /etc/network/interfaces at all, only /etc/netplan/50-cloud-init.yaml feeding systemd-networkd. Start with the services:

systemctl is-active NetworkManager systemd-networkd networking

The answers come back in the order you asked. This is our test server after we handed the NIC to NetworkManager for the nmcli section:

active
inactive
inactive

Two active lines are normal on a Debian desktop, because networking.service is a oneshot unit that stays “active” even when /etc/network/interfaces only configures lo. So don’t stop at systemctl. Decide by which tool actually names your NIC, as you’d see it on your own box:

  • The NIC has an auto, allow-hotplug or iface line in /etc/network/interfaces: ifupdown
  • networkctl list shows the NIC as configured: systemd-networkd (netplan on cloud images)
  • nmcli device status shows the NIC connected: NetworkManager

Now get the interface name. The examples use ens18, the name a virtio NIC gets on Proxmox, and yours may be eno1, enp3s0 or similar. List the links:

ip -br link

Replace ens18 with your own name in every command and file that follows. The VLAN interface is named <parent>.<vlan id>, so VLAN 10 on ens18 becomes ens18.10. That dotted form matters for ifupdown, which reads the parent and the VLAN ID straight out of the name.

A runtime VLAN made with ip link is the fastest way to prove the switch side works before you touch any config file, and it vanishes on reboot, so a mistake costs nothing. Create VLAN 10 on the parent NIC, give it an address and bring it up:

sudo ip link add link ens18 name ens18.10 type vlan id 10
sudo ip addr add 10.0.10.11/24 dev ens18.10
sudo ip link set ens18.10 up

You do not have to modprobe 8021q first. On our box lsmod | grep 8021q printed nothing before the ip link add and showed the module loaded right after it. The detailed link view is the one that proves the tag, so look for the vlan protocol 802.1Q id 10 line:

ip -d link show ens18.10

The @eth0 suffix in our capture names the parent, and the third line carries the protocol and VLAN ID (our first lab run, when the NIC was still eth0):

3: eth0.10@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether bc:24:11:b1:06:6e brd ff:ff:ff:ff:ff:ff promiscuity 0 allmulti 0 minmtu 0 maxmtu 65535
    vlan protocol 802.1Q id 10 <REORDER_HDR> addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 gso_ipv4_max_size 65536 gro_ipv4_max_size 65536 

If you can now ping another host on VLAN 10, the switch and the tag are right, and any later failure is in your config file, not the network. We rebooted after this test and ip -br link listed only lo and the parent NIC again, so a runtime VLAN is for testing only. To remove it without a reboot, run sudo ip link del ens18.10.

Make the VLAN interface on Debian persistent with ifupdown

ifupdown is the classic Debian method, and the one to use when your NIC already has a stanza in /etc/network/interfaces. Older guides tell you to install the vlan package and load 8021q from /etc/modules. Neither was needed here: with the ifupdown that ships in Debian 13, dpkg -l vlan reported no such package installed and the VLAN still came up, because ifupdown runs ip link add ... type vlan itself when the interface name has a dot in it.

If your server is a cloud or VPS image that runs netplan and systemd-networkd, skip this section and use the networkd one below. We converted such an image to ifupdown for this test, and it is a migration, not a VLAN step. Removing netplan’s YAML also removes the .link file that renamed the NIC to eth0, so on the next boot the NIC came up as ens18, our interfaces file still said eth0, and the box had no network until we fixed it from the console. udev picks the name from the first one it finds in the order onboard, slot, path: udevadm info showed ID_NET_NAME_SLOT=ens18 and ID_NET_NAME_PATH=enp0s18, and ens18 is what the kernel used.

On a server where ifupdown already owns the NIC, open the interfaces file:

sudo vim /etc/network/interfaces

Leave your existing parent stanza exactly as it is, whether it says auto or allow-hotplug, dhcp or static. Rewriting it is how a remote server loses its management address at the next reboot. Append only the VLAN stanza at the end of the file. It gets a static address and no gateway, since the default route should stay on the management side:

# VLAN 10 tagged on ens18
auto ens18.10
iface ens18.10 inet static
    address 10.0.10.11/24

For reference, this is the complete file our test server booted with. The ens18 stanza above the VLAN is our lab’s DHCP management setup, not something to copy over yours:

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# Untagged management network
auto ens18
iface ens18 inet dhcp

# VLAN 10 tagged on ens18
auto ens18.10
iface ens18.10 inet static
    address 10.0.10.11/24

Keep the dotted name, because that is what let ifupdown work out the parent and the VLAN ID on its own in our test. If your server only lives on the VLAN and the parent carries no untagged traffic, the parent becomes iface ens18 inet manual and the gateway line moves into the VLAN stanza; we did not test that layout. Bring the VLAN up without touching the parent:

sudo ifup ens18.10

With -v you can watch ifupdown do it: it brings up the parent first, then runs ip link add link eth0 name eth0.10 type vlan id 10 (our first run, on the old eth0 name), then adds the address. Reboot with a console open to prove it persists:

sudo reboot

After the reboot, check the addresses and the kernel’s VLAN table. A good result shows ens18.10@ens18 with your static address, and /proc/net/vlan/config lists the VLAN against its parent:

ip -br addr
sudo cat /proc/net/vlan/config

This was our server after the reboot, with networking.service active (our LAN address is replaced with 10.0.1.50):

lo               UNKNOWN        127.0.0.1/8 ::1/128
ens18            UP             10.0.1.50/24 fe80::deda:cd0a:4381:e22/64
ens18.10@ens18   UP             10.0.10.11/24 fe80::be24:11ff:fec2:3285/64
VLAN Dev name	 | VLAN ID
Name-Type: VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD
ens18.10       | 10  | ens18

Then check routing, because a VLAN that steals the default route breaks your SSH session in a way you’d blame on the switch.

ip route

The default route stays on the parent and the VLAN subnet sits on ens18.10 (LAN addresses replaced):

default via 10.0.1.1 dev ens18 proto dhcp src 10.0.1.50 metric 1002 
10.0.10.0/24 dev ens18.10 proto kernel scope link src 10.0.10.11 
10.0.1.0/24 dev ens18 proto dhcp scope link src 10.0.1.50 metric 1002 

If the default route moved to ens18.10, there is a gateway line in the VLAN stanza, and replies to SSH may now leave through the wrong interface. Remove it and run sudo ifdown ens18.10 && sudo ifup ens18.10.

Configure the VLAN with systemd-networkd

Use this method when systemd-networkd is the active service and you write its files by hand. A networkd VLAN needs three pieces: a .netdev that creates the VLAN device, a .network for its address, and a VLAN= line in the parent’s .network that attaches it. Forget that third piece and nothing errors. The VLAN just never appears, as the troubleshooting section shows. If netplan generates your networkd config, as on the cloud image, add a vlans: block to the netplan YAML instead, the same way the Ubuntu netplan VLAN guide does it, because the files under /run/systemd/network are regenerated on every boot.

Don’t create a second .network file for the parent. networkd applies only the first file that matches an interface, in lexical order, and ignores the rest, so a new parent file either replaces your existing management config or is shadowed by it, and then its VLAN= line is never read. Find the file that already matches the parent:

networkctl status ens18 | grep "Network File"

Open the file that line names and keep everything in it, adding one line to its [Network] section. If the line reads n/a, nothing matches the parent yet (the usual state when you are moving off ifupdown): create one parent file that copies your current management setup, DHCP= or Address= and Gateway=, plus the VLAN= line. That is what we did, and our new file was /etc/systemd/network/10-ens18.network.

sudo vim /etc/systemd/network/10-ens18.network

Our complete parent file is below. The DHCP=ipv4 management setup is our lab’s; the line you add to yours is VLAN=ens18.10, which names the child to create on top of the parent:

[Match]
Name=ens18

[Network]
DHCP=ipv4
VLAN=ens18.10

Next, the device definition. The systemd.netdev documentation lists Id= as compulsory, with a range of 0 to 4094:

sudo vim /etc/systemd/network/20-ens18.10.netdev

Add the VLAN device:

[NetDev]
Name=ens18.10
Kind=vlan

[VLAN]
Id=10

Then the address for the VLAN interface itself:

sudo vim /etc/systemd/network/30-ens18.10.network

A static address with no gateway, same as the ifupdown setup:

[Match]
Name=ens18.10

[Network]
Address=10.0.10.11/24

On a server where networkd already owns the NIC, apply the new files without a reboot:

sudo networkctl reload

Our test server came from the ifupdown section, so we also had to move the NIC between managers, and that is a separate job with its own risks. Two managers on one NIC is how you end up with two DHCP leases and a route you did not ask for, and our first ifup on the networkd-managed cloud image did exactly that. The DHCP lease can change too: our dhcpcd log shows it sending a DUID-LLT client ID, while networkd sends its own DUID by default, so the DHCP server may hand the host a new address after the switch. Only migrate with a console attached or a DHCP reservation in place, and check DNS afterwards (our image ran systemd-resolved, so name resolution kept working). These are the commands we used, with systemd-networkd already enabled on the image:

sudo systemctl disable networking
sudo mv /etc/network/interfaces /root/interfaces.ifupdown
sudo reboot

Check the result with networkctl. Both links should read routable and configured:

networkctl list

After the reboot the VLAN came up under networkd with networking.service inactive:

IDX LINK     TYPE     OPERATIONAL SETUP
  1 lo       loopback carrier     unmanaged
  2 ens18    ether    routable    configured
  3 ens18.10 vlan     routable    configured

3 links listed.

networkctl status ens18.10 goes one step further and names the .netdev and .network files it used, plus VLan Id: 10. That is the quickest way to catch a file that isn’t being picked up because of a typo in its name or [Match] section.

Add the VLAN with nmcli (NetworkManager)

On a Debian desktop, NetworkManager owns the wired NIC, and editing /etc/network/interfaces there backfires: Debian’s NetworkManager ships with [ifupdown] managed=false, so any interface listed in that file is taken away from NetworkManager. Use nmcli instead. We installed network-manager on the same test server, moved the networkd files out of /etc/systemd/network, disabled both systemd-networkd.service and systemd-networkd.socket, rebooted, and let NetworkManager pick up ens18 as “Wired connection 1”. One command creates the VLAN profile:

sudo nmcli connection add type vlan con-name vlan10 ifname ens18.10 vlan.parent ens18 vlan.id 10 ipv4.method manual ipv4.addresses 10.0.10.11/24 ipv6.method disabled

NetworkManager activates the profile straight away. List the connections to confirm it bound to the right device:

nmcli -f NAME,TYPE,DEVICE connection show

The vlan10 profile sits on ens18.10, next to the untagged wired connection:

NAME                TYPE      DEVICE
Wired connection 1  ethernet  ens18
vlan10              vlan      ens18.10
lo                  loopback  lo       

The profile is saved as /etc/NetworkManager/system-connections/vlan10.nmconnection, with id=10 and parent=ens18 under its [vlan] section, and that’s what brings it back after a reboot. We rebooted and nmcli connection show --active listed vlan10 as activated with 10.0.10.11/24 on ens18.10. To change the address later, use sudo nmcli connection modify vlan10 ipv4.addresses ... followed by sudo nmcli connection up vlan10.

Verify the frames are tagged

An address on ens18.10 proves the interface exists, not that frames leave with the tag. tcpdump on the parent NIC shows the 802.1Q header, which is the proof you want before blaming the switch. Install it, then start a ping to any address on the VLAN subnet from a second terminal while it listens:

sudo apt install tcpdump
sudo tcpdump -e -nn -c 2 -i ens18 vlan

Our ping went to 10.0.10.12, and each ARP request left ens18 with ethertype 802.1Q (0x8100) and vlan 10 in the header:

03:29:59.416286 bc:24:11:c2:32:85 > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 46: vlan 10, p 0, ethertype ARP (0x0806), Request who-has 10.0.10.12 tell 10.0.10.11, length 28
03:30:00.430693 bc:24:11:c2:32:85 > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 46: vlan 10, p 0, ethertype ARP (0x0806), Request who-has 10.0.10.12 tell 10.0.10.11, length 28

If the requests leave tagged but nothing answers, the problem is beyond this host: the switch port does not carry VLAN 10, or the peer sits on a different VLAN ID. The screenshot below pulls the persistent ifupdown state together, with the 802.1Q ID, the kernel VLAN table and the tagged frames:

Debian 13 VLAN interface ens18.10 with 802.1Q id 10 and tagged frames in tcpdump

Seeing a request with no vlan 10 in it means the packet left untagged on the parent, which usually happens when the address was added to ens18 instead of ens18.10.

Fix the errors we hit

Each of these came out of the lab runs above, with the exact message Debian 13 printed.

ifup: could not bring up parent interface eth0

This is the boot failure from the netplan to ifupdown switch. networking.service failed, and the journal showed eth0: interface not found from dhcpcd followed by ifup: could not bring up parent interface eth0, while ip -br link showed the NIC sitting DOWN as ens18. The parent name in the file has to match the real NIC name, and the VLAN name follows it. Fix both stanzas, then sudo systemctl restart networking from the console, since SSH is gone at that point.

RTNETLINK answers: File exists

You ran ip link add for a VLAN that already exists, often one that ifupdown or networkd created at boot. Check with ip -d link show ens18.10. If it is there with the right ID, you have nothing to add. Delete it with sudo ip link del ens18.10 only when you want to recreate it.

Error: 8021q: Invalid VLAN id.

We got this from type vlan id 4095. The 802.1Q field is 12 bits, and the kernel refuses 4095, so valid IDs stop at 4094. Use the ID configured on the switch, and avoid VLAN 1, which many switches use as the default untagged VLAN.

Cannot find device “eth1”

The parent named after link doesn’t exist. Copying commands from a guide written for another box is the usual cause. Run ip -br link and use the name it prints.

networkd VLAN never appears, no error

We removed the VLAN=ens18.10 line from the parent’s .network file, deleted the link and ran networkctl reload. The .netdev was still in place, but networkctl list showed only lo and ens18, and the journal had no complaint about it. Putting the line back and reloading created ens18.10 within seconds. When a networkd VLAN is missing, check the parent’s file first.

Run VLAN interfaces in production

The config itself is short. What bites later is everything around it. These are the points we’d check on any server that carries more than one VLAN:

  • One network manager per NIC, with the others disabled
  • Exactly one default gateway, normally on the management network
  • Matching MTU on parent, VLAN and switch port, with the parent never below the VLAN
  • Console access tested before any change to the parent NIC
  • IP forwarding left off unless the host really routes between VLANs

The last point is worth a sentence because older versions of this guide set net.ipv4.ip_forward=1 on every host. A server with addresses on two VLANs and forwarding on becomes a router between them, which quietly defeats the separation the VLANs were built for, so only enable it on a box whose job is routing. If the host runs VMs or containers that need to sit on VLAN 10, put a bridge on top of the VLAN interface instead, as shown in the guide to a Linux bridge on a VLAN interface in Debian. For RHEL-family hosts the same tagging is done through NetworkManager keyfiles, covered in VLAN tagging on RHEL, CentOS and Fedora, and the switch side of trunk ports is in the Cisco VLAN configuration guide.

Keep reading

Free CCNA 200-301 Practice Questions with Answers Networking Free CCNA 200-301 Practice Questions with Answers Configure Samba File Share on Debian 13 / 12 Debian Configure Samba File Share on Debian 13 / 12 Backup and Restore Linux Systems with Timeshift Debian Backup and Restore Linux Systems with Timeshift Upgrade a Single Package on Ubuntu / Debian with apt Debian Upgrade a Single Package on Ubuntu / Debian with apt APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian Cheat Sheets APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian How To Install PHP 7.4 on Debian 10 / Debian 9 Php How To Install PHP 7.4 on Debian 10 / Debian 9

Leave a Comment

Press ESC to close