VLAN tagging using the IEEE 802.1Q standard allows a single physical network interface to carry traffic for multiple isolated networks simultaneously. Each VLAN is identified by a numeric tag (1–4094) inserted into Ethernet frames, and the operating system presents each tagged VLAN as a separate logical interface with its own IP address and routing table. On RHEL 9, NetworkManager manages 802.1Q VLAN interfaces natively through nmcli, making VLAN configuration consistent and persistent across reboots. This tutorial covers creating VLAN interfaces, assigning IPs, configuring firewalld zones per VLAN, and explains the required switch-side configuration.

Prerequisites

  • RHEL 9 server with root or sudo access
  • At least one physical or virtual network interface (e.g., ens3) connected to a trunk port on a managed switch
  • NetworkManager running (systemctl status NetworkManager)
  • Knowledge of which VLAN IDs are configured on the upstream switch’s trunk port

Step 1 — Create a VLAN Interface

Use nmcli to create the VLAN connection. The dev parameter specifies the underlying physical interface, and id is the 802.1Q VLAN tag number. NetworkManager automatically creates a logical interface named ens3.10 for VLAN 10 on ens3.

# Create VLAN 10 on top of ens3 (management network)
sudo nmcli con add 
    type vlan 
    con-name vlan10 
    dev ens3 
    id 10

# Assign a static IP to VLAN 10
sudo nmcli con mod vlan10 
    ipv4.method manual 
    ipv4.addresses "10.0.10.1/24" 
    ipv4.gateway "10.0.10.254" 
    ipv4.dns "10.0.10.53"

# Bring the VLAN interface up
sudo nmcli con up vlan10

# Verify the interface exists and has the correct IP
ip addr show ens3.10

The ip addr show output should display the interface as ens3.10@ens3, confirming it is a VLAN sub-interface on top of ens3. The @ens3 suffix shows the parent interface relationship.

Step 2 — Create Additional VLANs for Separated Networks

In a typical server virtualisation environment you will have multiple VLANs for traffic separation. The following example adds a production VLAN (20) and a DMZ VLAN (30) to the same physical interface.

# VLAN 20 — Production network
sudo nmcli con add type vlan con-name vlan20 dev ens3 id 20
sudo nmcli con mod vlan20 
    ipv4.method manual 
    ipv4.addresses "10.0.20.1/24" 
    ipv4.gateway "10.0.20.254"
sudo nmcli con up vlan20

# VLAN 30 — DMZ network
sudo nmcli con add type vlan con-name vlan30 dev ens3 id 30
sudo nmcli con mod vlan30 
    ipv4.method manual 
    ipv4.addresses "10.0.30.1/24"
sudo nmcli con up vlan30

# Confirm all three VLAN interfaces are active
ip link show | grep ens3

You should see ens3.10@ens3, ens3.20@ens3, and ens3.30@ens3 in the output. Each VLAN interface has its own independent routing table entry visible in ip route show.

Step 3 — Configure firewalld Zones per VLAN

Assigning each VLAN interface to a different firewalld zone enforces strict inter-VLAN traffic policies. The management VLAN gets the trusted zone, production gets the internal zone, and the DMZ gets the dmz zone with only HTTP/HTTPS permitted.

# Assign VLAN 10 (management) to the trusted zone
sudo firewall-cmd --permanent --zone=trusted --add-interface=ens3.10

# Assign VLAN 20 (production) to the internal zone
sudo firewall-cmd --permanent --zone=internal --add-interface=ens3.20

# Assign VLAN 30 (DMZ) to the dmz zone
sudo firewall-cmd --permanent --zone=dmz --add-interface=ens3.30

# Allow HTTP and HTTPS in the DMZ zone
sudo firewall-cmd --permanent --zone=dmz --add-service=http
sudo firewall-cmd --permanent --zone=dmz --add-service=https

# Reload to apply all changes
sudo firewall-cmd --reload

# Verify zone assignments
sudo firewall-cmd --get-active-zones

Traffic arriving on ens3.30 is now evaluated against the strict DMZ zone rules while management traffic on ens3.10 flows unrestricted through the trusted zone.

Step 4 — Verify Routing and Connectivity

Confirm the kernel has created routes for each VLAN subnet and that the interfaces respond to ping from the correct source IP.

# List all routes including per-VLAN subnets
ip route show

# Check that each VLAN interface shows UP
ip link show ens3.10
ip link show ens3.20
ip link show ens3.30

# Test connectivity from the management VLAN subnet
ping -I ens3.10 -c 3 10.0.10.254

# Display full interface details including VLAN ID
ip -d link show ens3.10

The ip -d link show command prints detailed interface info including the VLAN protocol (802.1Q), the tag ID, and the parent device, which is useful for auditing VLAN assignments.

Step 5 — Switch-Side Configuration Notes

The server-side VLAN configuration only works correctly when the upstream switch port is configured as a trunk port that allows all the required VLAN tags. An access port only passes untagged frames for a single VLAN and will not work with 802.1Q sub-interfaces.

# Cisco IOS example — configure the switch port as a trunk
interface GigabitEthernet0/1
  switchport mode trunk
  switchport trunk allowed vlan 10,20,30
  switchport trunk native vlan 1

# Linux bridge / Open vSwitch example — add a trunk port
ovs-vsctl add-port br0 ens3 trunk=10,20,30

# For KVM/libvirt virtual machines — the bridge must be on the trunk interface
# so VMs can tag their own frames. Do NOT assign an IP to the parent ens3
# if all traffic will be VLAN-tagged.
ip addr flush dev ens3

On the native VLAN: frames on the native VLAN travel untagged across the trunk. If your switch has a non-default native VLAN, ensure the Linux parent interface (ens3) does not have an IP — otherwise untagged management traffic may arrive on the wrong interface.

Step 6 — Make Configuration Persistent and Review

NetworkManager connection profiles are stored under /etc/NetworkManager/system-connections/ and are persistent by default. Verify the VLAN configurations will survive a reboot.

# List all NetworkManager connection profiles
nmcli con show

# Inspect the stored VLAN10 profile
sudo cat /etc/NetworkManager/system-connections/vlan10.nmconnection

# Reload all connection profiles (useful after manual file edits)
sudo nmcli con reload

# To remove a VLAN interface when no longer needed
sudo nmcli con down vlan30
sudo nmcli con delete vlan30

Each VLAN connection file contains the [vlan] section with id= (the tag) and parent= (the physical interface). Editing these files directly and running nmcli con reload is an alternative to nmcli con mod for bulk changes.

Conclusion

You have configured 802.1Q VLAN tagging on RHEL 9 using NetworkManager, created multiple VLAN sub-interfaces on a single physical NIC, assigned each to a dedicated firewalld zone, and reviewed the required switch trunk port settings. VLAN segmentation is a fundamental building block for multi-tenant server environments, virtual machine networking, and network security isolation.

Next steps: How to Configure BGP Routing with BIRD on RHEL 9, How to Configure Network Bonding and Teaming on RHEL 9, and How to Set Up Pacemaker and Corosync for High Availability on RHEL 9.