diff --git a/data/templates/accel-ppp/ipoe.config.j2 b/data/templates/accel-ppp/ipoe.config.j2
index 9729b295e..81f63c53b 100644
--- a/data/templates/accel-ppp/ipoe.config.j2
+++ b/data/templates/accel-ppp/ipoe.config.j2
@@ -1,109 +1,109 @@
 {# j2lint: disable=operator-enclosed-by-spaces #}
 ### generated by ipoe.py ###
 [modules]
 log_syslog
 ipoe
 shaper
 {# Common authentication backend definitions #}
 {% include 'accel-ppp/config_modules_auth_mode.j2' %}
 ippool
 ipv6pool
 ipv6_nd
 ipv6_dhcp
 {% if snmp is vyos_defined %}
 net-snmp
 {% endif %}
 {% if limits is vyos_defined %}
 connlimit
 {% endif %}
 {% if extended_scripts is vyos_defined %}
 pppd_compat
 {% endif %}
 
 [core]
 thread-count={{ thread_count }}
 
 [common]
 {% if max_concurrent_sessions is vyos_defined %}
 max-starting={{ max_concurrent_sessions }}
 {% endif %}
 
 
 [log]
 syslog=accel-ipoe,daemon
 copy=1
 {% if log.level is vyos_defined %}
 level={{ log.level }}
 {% endif %}
 
 [ipoe]
 verbose=1
 {% if interface is vyos_defined %}
 {%     for iface, iface_config in interface.items() %}
 {%         set tmp = 'interface=' %}
 {%         if iface_config.vlan is vyos_defined %}
 {%             set tmp = tmp ~ 're:^' ~ iface ~ '\.' ~ iface_config.vlan | range_to_regex ~ '$' %}
 {%         else %}
 {%             set tmp = tmp ~ iface %}
 {%         endif %}
 {%         set shared = '' %}
 {%         if iface_config.network is vyos_defined('shared') %}
 {%             set shared = 'shared=1,' %}
 {%         elif iface_config.network is vyos_defined('vlan') %}
 {%             set shared = 'shared=0,' %}
 {%         endif %}
 {%         set range = 'range=' ~ iface_config.client_subnet ~ ',' if iface_config.client_subnet is vyos_defined else '' %}
 {%         set relay = ',' ~ 'relay=' ~ iface_config.external_dhcp.dhcp_relay  if iface_config.external_dhcp.dhcp_relay is vyos_defined else '' %}
 {%         set giaddr = ',' ~ 'giaddr=' ~ iface_config.external_dhcp.giaddr if iface_config.external_dhcp.giaddr is vyos_defined else '' %}
 {{ tmp }},{{ shared }}mode={{ iface_config.mode | upper }},ifcfg=1,{{ range }}start=dhcpv4,ipv6=1{{ relay }}{{ giaddr }}
-{%         if iface_config.vlan is vyos_defined %}
+{%         if iface_config.vlan_mon is vyos_defined %}
 vlan-mon={{ iface }},{{ iface_config.vlan | join(',') }}
 {%         endif %}
 {%     endfor %}
 {% endif %}
 {% if authentication.mode is vyos_defined('noauth') %}
 noauth=1
 {% elif authentication.mode is vyos_defined('local') %}
 username=ifname
 password=csid
 {% endif %}
 {% if default_pool is vyos_defined %}
 ip-pool={{ default_pool }}
 {% endif %}
 {% if default_ipv6_pool is vyos_defined %}
 ipv6-pool={{ default_ipv6_pool }}
 ipv6-pool-delegate={{ default_ipv6_pool }}
 {% endif %}
 {% if gateway_address is vyos_defined %}
 {%     for gw_addr in gateway_address %}
 gw-ip-address={{ gw_addr }}
 {%     endfor %}
 {% endif %}
 proxy-arp=1
 
 {# Common IP pool definitions #}
 {% include 'accel-ppp/config_ip_pool.j2' %}
 
 {# Common IPv6 pool definitions #}
 {% include 'accel-ppp/config_ipv6_pool.j2' %}
 
 {# Common DNS name-server definition #}
 {% include 'accel-ppp/config_name_server.j2' %}
 
 {# Common chap-secrets and RADIUS server/option definitions #}
 {% include 'accel-ppp/config_chap_secrets_radius.j2' %}
 
 {# Common RADIUS shaper configuration #}
 {% include 'accel-ppp/config_shaper_radius.j2' %}
 
 {# Common Extended scripts configuration #}
 {% include 'accel-ppp/config_extended_scripts.j2' %}
 
 {# Common Limits configuration #}
 {% include 'accel-ppp/config_limits.j2' %}
 
 {# Common SNMP definitions #}
 {% include 'accel-ppp/config_snmp.j2' %}
 
 [cli]
 tcp=127.0.0.1:2002
diff --git a/data/templates/accel-ppp/pppoe.config.j2 b/data/templates/accel-ppp/pppoe.config.j2
index 6711f2ec9..6a387bd46 100644
--- a/data/templates/accel-ppp/pppoe.config.j2
+++ b/data/templates/accel-ppp/pppoe.config.j2
@@ -1,125 +1,127 @@
 ### generated by accel_pppoe.py ###
 [modules]
 log_syslog
 pppoe
 shaper
 {# Common authentication backend definitions #}
 {% include 'accel-ppp/config_modules_auth_mode.j2' %}
 ippool
 {# Common IPv6 definitions #}
 {% include 'accel-ppp/config_modules_ipv6.j2' %}
 {# Common authentication protocols (pap, chap ...) #}
 {% include 'accel-ppp/config_modules_auth_protocols.j2' %}
 {% if snmp is vyos_defined %}
 net-snmp
 {% endif %}
 {% if limits is vyos_defined %}
 connlimit
 {% endif %}
 {% if extended_scripts is vyos_defined %}
 sigchld
 pppd_compat
 {% endif %}
 
 [core]
 thread-count={{ thread_count }}
 
 [log]
 syslog=accel-pppoe,daemon
 copy=1
 {% if log.level is vyos_defined %}
 level={{ log.level }}
 {% endif %}
 
 {% if authentication.mode is vyos_defined("noauth") %}
 [auth]
 noauth=1
 {% endif %}
 
 [client-ip-range]
 0.0.0.0/0
 
 [common]
 {% if session_control is vyos_defined and session_control is not vyos_defined('disable') %}
 single-session={{ session_control }}
 {% endif %}
 {% if max_concurrent_sessions is vyos_defined %}
 max-starting={{ max_concurrent_sessions }}
 {% endif %}
 
 [pppoe]
 verbose=1
 ac-name={{ access_concentrator }}
 {% if interface is vyos_defined %}
 {%     for iface, iface_config in interface.items() %}
 {%         if iface_config.vlan is not vyos_defined %}
 interface={{ iface }}
 {%         else %}
 {%             for vlan in iface_config.vlan %}
 interface=re:^{{ iface }}\.{{ vlan | range_to_regex }}$
 {%             endfor %}
+{%             if iface_config.vlan_mon is vyos_defined %}
 vlan-mon={{ iface }},{{ iface_config.vlan | join(',') }}
+{%             endif %}
 {%         endif %}
 {%     endfor %}
 {% endif %}
 {% if service_name %}
 service-name={{ service_name | join(',') }}
 {% endif %}
 {% if pado_delay %}
 {%     set delay_without_sessions = pado_delay.delays_without_sessions[0] | default('0') %}
 {%     set pado_delay_param = namespace(value=delay_without_sessions) %}
 {%     for delay, sessions in pado_delay.delays_with_sessions | sort(attribute='1') %}
 {%         if not delay == 'disable' %}
 {%             set pado_delay_param.value = pado_delay_param.value + ',' + delay + ':' + sessions | string %}
 {%         else %}
 {%             set pado_delay_param.value = pado_delay_param.value + ',-1:' + sessions | string %}
 {%         endif %}
 {%     endfor %}
 pado-delay={{ pado_delay_param.value }}
 {% endif %}
 {% if authentication.radius.called_sid_format is vyos_defined %}
 called-sid={{ authentication.radius.called_sid_format }}
 {% endif %}
 {% if authentication.mode is vyos_defined("noauth") %}
 noauth=1
 {% endif %}
 {% if default_pool is vyos_defined %}
 ip-pool={{ default_pool }}
 {% endif %}
 {% if default_ipv6_pool is vyos_defined %}
 ipv6-pool={{ default_ipv6_pool }}
 ipv6-pool-delegate={{ default_ipv6_pool }}
 {% endif %}
 
 {# Common IP pool definitions #}
 {% include 'accel-ppp/config_ip_pool.j2' %}
 
 {# Common IPv6 pool definitions #}
 {% include 'accel-ppp/config_ipv6_pool.j2' %}
 
 {# Common DNS name-server definition #}
 {% include 'accel-ppp/config_name_server.j2' %}
 
 {# Common wins-server definition #}
 {% include 'accel-ppp/config_wins_server.j2' %}
 
 {# Common chap-secrets and RADIUS server/option definitions #}
 {% include 'accel-ppp/config_chap_secrets_radius.j2' %}
 
 {# Common ppp-options definitions #}
 {% include 'accel-ppp/ppp-options.j2' %}
 
 {# Common RADIUS shaper configuration #}
 {% include 'accel-ppp/config_shaper_radius.j2' %}
 
 {# Common Extended scripts configuration #}
 {% include 'accel-ppp/config_extended_scripts.j2' %}
 
 {# Common Limits configuration #}
 {% include 'accel-ppp/config_limits.j2' %}
 
 {# Common SNMP definitions #}
 {% include 'accel-ppp/config_snmp.j2' %}
 
 [cli]
 tcp=127.0.0.1:2001
diff --git a/interface-definitions/include/accel-ppp/vlan-mon.xml.i b/interface-definitions/include/accel-ppp/vlan-mon.xml.i
new file mode 100644
index 000000000..d5bacb0d1
--- /dev/null
+++ b/interface-definitions/include/accel-ppp/vlan-mon.xml.i
@@ -0,0 +1,8 @@
+<!-- include start from accel-ppp/vlan-mon.xml.i -->
+<leafNode name="vlan-mon">
+  <properties>
+    <help>Automatically create VLAN interfaces</help>
+    <valueless/>
+  </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/version/ipoe-server-version.xml.i b/interface-definitions/include/version/ipoe-server-version.xml.i
index 659433382..b7718fc5e 100644
--- a/interface-definitions/include/version/ipoe-server-version.xml.i
+++ b/interface-definitions/include/version/ipoe-server-version.xml.i
@@ -1,3 +1,3 @@
 <!-- include start from include/version/ipoe-server-version.xml.i -->
-<syntaxVersion component='ipoe-server' version='3'></syntaxVersion>
+<syntaxVersion component='ipoe-server' version='4'></syntaxVersion>
 <!-- include end -->
diff --git a/interface-definitions/include/version/pppoe-server-version.xml.i b/interface-definitions/include/version/pppoe-server-version.xml.i
index 61de1277a..2e020faa3 100644
--- a/interface-definitions/include/version/pppoe-server-version.xml.i
+++ b/interface-definitions/include/version/pppoe-server-version.xml.i
@@ -1,3 +1,3 @@
 <!-- include start from include/version/pppoe-server-version.xml.i -->
-<syntaxVersion component='pppoe-server' version='10'></syntaxVersion>
+<syntaxVersion component='pppoe-server' version='11'></syntaxVersion>
 <!-- include end -->
diff --git a/interface-definitions/service_ipoe-server.xml.in b/interface-definitions/service_ipoe-server.xml.in
index c7542f0d0..25bc43cc6 100644
--- a/interface-definitions/service_ipoe-server.xml.in
+++ b/interface-definitions/service_ipoe-server.xml.in
@@ -1,197 +1,198 @@
 <?xml version="1.0"?>
 <interfaceDefinition>
   <node name="service">
     <children>
       <node name="ipoe-server" owner="${vyos_conf_scripts_dir}/service_ipoe-server.py">
         <properties>
           <help>Internet Protocol over Ethernet (IPoE) Server</help>
           <priority>900</priority>
         </properties>
         <children>
           <node name="authentication">
             <properties>
               <help>Client authentication methods</help>
             </properties>
             <children>
               #include <include/accel-ppp/auth-mode.xml.i>
               <tagNode name="interface">
                 <properties>
                   <help>Network interface for client MAC addresses</help>
                   <completionHelp>
                     <script>${vyos_completion_dir}/list_interfaces</script>
                   </completionHelp>
                 </properties>
                 <children>
                   <tagNode name="mac">
                     <properties>
                       <help>Media Access Control (MAC) address</help>
                       <valueHelp>
                         <format>macaddr</format>
                         <description>Hardware (MAC) address</description>
                       </valueHelp>
                       <constraint>
                         <validator name="mac-address"/>
                       </constraint>
                     </properties>
                     <children>
                       <node name="rate-limit">
                         <properties>
                           <help>Upload/Download speed limits</help>
                         </properties>
                         <children>
                           <leafNode name="upload">
                             <properties>
                               <help>Upload bandwidth limit in kbits/sec</help>
                               <constraint>
                                 <validator name="numeric" argument="--range  1-4294967295"/>
                               </constraint>
                             </properties>
                           </leafNode>
                           <leafNode name="download">
                             <properties>
                               <help>Download bandwidth limit in kbits/sec</help>
                               <constraint>
                                 <validator name="numeric" argument="--range  1-4294967295"/>
                               </constraint>
                             </properties>
                           </leafNode>
                         </children>
                       </node>
                       <leafNode name="vlan">
                         <properties>
                           <help>VLAN monitor for automatic creation of VLAN interfaces</help>
                           <valueHelp>
                             <format>u32:1-4094</format>
                             <description>Client VLAN id</description>
                           </valueHelp>
                           <constraint>
                             <validator name="numeric" argument="--range 1-4094"/>
                           </constraint>
                           <constraintErrorMessage>VLAN IDs need to be in range 1-4094</constraintErrorMessage>
                         </properties>
                       </leafNode>
                     </children>
                   </tagNode>
                 </children>
               </tagNode>
               #include <include/radius-auth-server-ipv4.xml.i>
               #include <include/accel-ppp/radius-additions.xml.i>
               <node name="radius">
                 <children>
                   #include <include/accel-ppp/radius-additions-rate-limit.xml.i>
                 </children>
               </node>
             </children>
           </node>
           <tagNode name="interface">
             <properties>
               <help>Interface to listen dhcp or unclassified packets</help>
               <completionHelp>
                 <script>${vyos_completion_dir}/list_interfaces</script>
               </completionHelp>
             </properties>
             <children>
               <leafNode name="mode">
                 <properties>
                   <help>Client connectivity mode</help>
                   <completionHelp>
                     <list>l2 l3</list>
                   </completionHelp>
                   <valueHelp>
                     <format>l2</format>
                     <description>Client located on same interface as server</description>
                   </valueHelp>
                   <valueHelp>
                     <format>l3</format>
                     <description>Client located behind a router</description>
                   </valueHelp>
                   <constraint>
                     <regex>(l2|l3)</regex>
                   </constraint>
                 </properties>
                 <defaultValue>l2</defaultValue>
               </leafNode>
               <leafNode name="network">
                 <properties>
                   <help>Enables clients to share the same network or each client has its own vlan</help>
                   <completionHelp>
                     <list>shared vlan</list>
                   </completionHelp>
                   <constraint>
                     <regex>(shared|vlan)</regex>
                   </constraint>
                   <valueHelp>
                     <format>shared</format>
                     <description>Multiple clients share the same network</description>
                   </valueHelp>
                   <valueHelp>
                     <format>vlan</format>
                     <description>One VLAN per client</description>
                   </valueHelp>
                 </properties>
                 <defaultValue>shared</defaultValue>
               </leafNode>
               <leafNode name="client-subnet">
                 <properties>
                   <help>Client address pool</help>
                   <valueHelp>
                     <format>ipv4net</format>
                     <description>IPv4 address and prefix length</description>
                   </valueHelp>
                   <constraint>
                     <validator name="ipv4-prefix"/>
                   </constraint>
                 </properties>
               </leafNode>
               <node name="external-dhcp">
                 <properties>
                   <help>DHCP requests will be forwarded</help>
                 </properties>
                 <children>
                   <leafNode name="dhcp-relay">
                     <properties>
                       <help>DHCP Server the request will be redirected to.</help>
                       <valueHelp>
                         <format>ipv4</format>
                         <description>IPv4 address of the DHCP Server</description>
                       </valueHelp>
                       <constraint>
                         <validator name="ipv4-address"/>
                       </constraint>
                     </properties>
                   </leafNode>
                   <leafNode name="giaddr">
                     <properties>
                       <help>Relay Agent IPv4 Address</help>
                       <valueHelp>
                         <format>ipv4</format>
                         <description>Gateway IP address</description>
                       </valueHelp>
                       <constraint>
                         <validator name="ipv4-address"/>
                       </constraint>
                     </properties>
                   </leafNode>
                 </children>
               </node>
               #include <include/accel-ppp/vlan.xml.i>
+              #include <include/accel-ppp/vlan-mon.xml.i>
             </children>
           </tagNode>
           #include <include/accel-ppp/client-ip-pool.xml.i>
           #include <include/accel-ppp/client-ipv6-pool.xml.i>
           #include <include/accel-ppp/default-pool.xml.i>
           #include <include/accel-ppp/default-ipv6-pool.xml.i>
           #include <include/accel-ppp/extended-scripts.xml.i>
           #include <include/accel-ppp/gateway-address-multi.xml.i>
           #include <include/accel-ppp/limits.xml.i>
           #include <include/accel-ppp/max-concurrent-sessions.xml.i>
           #include <include/accel-ppp/shaper.xml.i>
           #include <include/accel-ppp/snmp.xml.i>
           #include <include/generic-description.xml.i>
           #include <include/name-server-ipv4-ipv6.xml.i>
           #include <include/accel-ppp/log.xml.i>
         </children>
       </node>
     </children>
   </node>
 </interfaceDefinition>
diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in
index 81228938f..31562e278 100644
--- a/interface-definitions/service_pppoe-server.xml.in
+++ b/interface-definitions/service_pppoe-server.xml.in
@@ -1,161 +1,162 @@
 <?xml version="1.0"?>
 <interfaceDefinition>
   <node name="service">
     <children>
       <node name="pppoe-server" owner="${vyos_conf_scripts_dir}/service_pppoe-server.py">
         <properties>
           <help>Point to Point over Ethernet (PPPoE) Server</help>
           <priority>900</priority>
         </properties>
         <children>
           #include <include/pppoe-access-concentrator.xml.i>
           <leafNode name="access-concentrator">
             <defaultValue>vyos-ac</defaultValue>
           </leafNode>
           <node name="authentication">
             <properties>
               <help>Authentication for remote access PPPoE Server</help>
             </properties>
             <children>
               #include <include/accel-ppp/auth-local-users.xml.i>
               #include <include/accel-ppp/auth-mode.xml.i>
               #include <include/accel-ppp/auth-protocols.xml.i>
               #include <include/radius-auth-server-ipv4.xml.i>
               #include <include/accel-ppp/radius-additions.xml.i>
               <node name="radius">
                 <children>
                   #include <include/accel-ppp/radius-additions-rate-limit.xml.i>
                   <leafNode name="called-sid-format">
                     <properties>
                       <help>Format of Called-Station-Id attribute</help>
                       <completionHelp>
                         <list>ifname ifname:mac</list>
                       </completionHelp>
                       <constraint>
                         <regex>(ifname|ifname:mac)</regex>
                       </constraint>
                       <constraintErrorMessage>Invalid Called-Station-Id format</constraintErrorMessage>
                       <valueHelp>
                         <format>ifname</format>
                         <description>NAS-Port-Id - should contain root interface name (NAS-Port-Id=eth1)</description>
                       </valueHelp>
                       <valueHelp>
                         <format>ifname:mac</format>
                         <description>NAS-Port-Id - should contain root interface name and mac address (NAS-Port-Id=eth1:00:00:00:00:00:00)</description>
                       </valueHelp>
                     </properties>
                   </leafNode>
                 </children>
               </node>
             </children>
           </node>
           <tagNode name="interface">
             <properties>
               <help>interface(s) to listen on</help>
               <completionHelp>
                 <script>${vyos_completion_dir}/list_interfaces</script>
               </completionHelp>
             </properties>
             <children>
               #include <include/accel-ppp/vlan.xml.i>
+              #include <include/accel-ppp/vlan-mon.xml.i>
             </children>
           </tagNode>
           <leafNode name="service-name">
             <properties>
               <help>Service name</help>
               <constraint>
                 <regex>[a-zA-Z0-9\-]{1,100}</regex>
               </constraint>
               <constraintErrorMessage>Service-name can contain aplhanumerical characters and dashes only (max. 100)</constraintErrorMessage>
               <multi/>
             </properties>
           </leafNode>
           <tagNode name="pado-delay">
             <properties>
               <help>PADO delays</help>
               <valueHelp>
                 <format>disable</format>
                 <description>Disable new connections</description>
               </valueHelp>
               <completionHelp>
                 <list>disable</list>
               </completionHelp>
               <valueHelp>
                 <format>u32:1-999999</format>
                 <description>Number in ms</description>
               </valueHelp>
               <constraint>
                 <validator name="numeric" argument="--range 1-999999"/>
                 <regex>disable</regex>
               </constraint>
               <constraintErrorMessage>Invalid PADO delay</constraintErrorMessage>
             </properties>
             <children>
               <leafNode name="sessions">
                 <properties>
                   <help>Number of sessions</help>
                   <valueHelp>
                     <format>u32:1-999999</format>
                     <description>Number of sessions</description>
                   </valueHelp>
                   <constraint>
                     <validator name="numeric" argument="--range 1-999999"/>
                   </constraint>
                   <constraintErrorMessage>Invalid number of delayed sessions</constraintErrorMessage>
                 </properties>
               </leafNode>
             </children>
           </tagNode>
           <leafNode name="session-control">
             <properties>
               <help>control sessions count</help>
               <constraint>
                 <regex>(deny|disable|replace)</regex>
               </constraint>
               <constraintErrorMessage>Invalid value</constraintErrorMessage>
               <valueHelp>
                 <format>disable</format>
                 <description>Disables session control</description>
               </valueHelp>
               <valueHelp>
                 <format>deny</format>
                 <description>Deny second session authorization</description>
               </valueHelp>
               <valueHelp>
                 <format>replace</format>
                 <description>Terminate first session when second is authorized</description>
               </valueHelp>
               <completionHelp>
                 <list>deny disable replace</list>
               </completionHelp>
             </properties>
             <defaultValue>replace</defaultValue>
           </leafNode>
           #include <include/accel-ppp/client-ip-pool.xml.i>
           #include <include/accel-ppp/client-ipv6-pool.xml.i>
           #include <include/accel-ppp/default-pool.xml.i>
           #include <include/accel-ppp/default-ipv6-pool.xml.i>
           #include <include/accel-ppp/extended-scripts.xml.i>
           #include <include/accel-ppp/gateway-address.xml.i>
           #include <include/accel-ppp/limits.xml.i>
           #include <include/accel-ppp/max-concurrent-sessions.xml.i>
           #include <include/accel-ppp/mtu-128-16384.xml.i>
           #include <include/accel-ppp/ppp-options.xml.i>
           <node name="ppp-options">
             <children>
               <leafNode name="min-mtu">
                 <defaultValue>1280</defaultValue>
               </leafNode>
             </children>
           </node>
           #include <include/accel-ppp/shaper.xml.i>
           #include <include/accel-ppp/snmp.xml.i>
           #include <include/accel-ppp/wins-server.xml.i>
           #include <include/generic-description.xml.i>
           #include <include/name-server-ipv4-ipv6.xml.i>
           #include <include/accel-ppp/log.xml.i>
         </children>
       </node>
     </children>
   </node>
 </interfaceDefinition>
diff --git a/smoketest/scripts/cli/test_service_ipoe-server.py b/smoketest/scripts/cli/test_service_ipoe-server.py
index 5f1cf9ad1..be03179bf 100755
--- a/smoketest/scripts/cli/test_service_ipoe-server.py
+++ b/smoketest/scripts/cli/test_service_ipoe-server.py
@@ -1,240 +1,272 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2022-2024 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
 # published by the Free Software Foundation.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import re
 import unittest
 
 from collections import OrderedDict
 from base_accel_ppp_test import BasicAccelPPPTest
 from vyos.configsession import ConfigSessionError
 from vyos.utils.process import cmd
+from vyos.template import range_to_regex
 from configparser import ConfigParser
 from configparser import RawConfigParser
 
 ac_name = "ACN"
 interface = "eth0"
 
 
 class MultiOrderedDict(OrderedDict):
     # Accel-ppp has duplicate keys in config file (gw-ip-address)
     # This class is used to define dictionary which can contain multiple values
     # in one key.
     def __setitem__(self, key, value):
         if isinstance(value, list) and key in self:
             self[key].extend(value)
         else:
             super(OrderedDict, self).__setitem__(key, value)
 
 
 class TestServiceIPoEServer(BasicAccelPPPTest.TestCase):
     @classmethod
     def setUpClass(cls):
         cls._base_path = ["service", "ipoe-server"]
         cls._config_file = "/run/accel-pppd/ipoe.conf"
         cls._chap_secrets = "/run/accel-pppd/ipoe.chap-secrets"
         cls._protocol_section = "ipoe"
 
         # call base-classes classmethod
         super(TestServiceIPoEServer, cls).setUpClass()
 
     def verify(self, conf):
         super().verify(conf)
 
         # Validate configuration values
         accel_modules = list(conf["modules"].keys())
         self.assertIn("log_syslog", accel_modules)
         self.assertIn("ipoe", accel_modules)
         self.assertIn("shaper", accel_modules)
         self.assertIn("ipv6pool", accel_modules)
         self.assertIn("ipv6_nd", accel_modules)
         self.assertIn("ipv6_dhcp", accel_modules)
         self.assertIn("ippool", accel_modules)
 
     def initial_gateway_config(self):
         self._gateway = "192.0.2.1/24"
         super().initial_gateway_config()
 
     def initial_auth_config(self):
         self.set(["authentication", "mode", "noauth"])
 
     def basic_protocol_specific_config(self):
         self.set(["interface", interface, "client-subnet", "192.168.0.0/24"])
 
     def test_accel_local_authentication(self):
         mac_address = "08:00:27:2f:d8:06"
         self.set(["authentication", "interface", interface, "mac", mac_address])
         self.set(["authentication", "mode", "local"])
 
         # No IPoE interface configured
         with self.assertRaises(ConfigSessionError):
             self.cli_commit()
 
         # Test configuration of local authentication for PPPoE server
         self.basic_config()
         # Rewrite authentication from basic_config
         self.set(["authentication", "interface", interface, "mac", mac_address])
         self.set(["authentication", "mode", "local"])
         # commit changes
         self.cli_commit()
 
         # Validate configuration values
         conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False)
         conf.read(self._config_file)
 
         # check proper path to chap-secrets file
         self.assertEqual(conf["chap-secrets"]["chap-secrets"], self._chap_secrets)
 
         accel_modules = list(conf["modules"].keys())
         self.assertIn("chap-secrets", accel_modules)
 
         # basic verification
         self.verify(conf)
 
         # check local users
         tmp = cmd(f"sudo cat {self._chap_secrets}")
         regex = f"{interface}\s+\*\s+{mac_address}\s+\*"
         tmp = re.findall(regex, tmp)
         self.assertTrue(tmp)
 
     def test_accel_ipv4_pool(self):
         self.basic_config(is_gateway=False, is_client_pool=False)
 
         gateway = ["172.16.0.1/25", "192.0.2.1/24"]
         subnet = "172.16.0.0/24"
         first_pool = "POOL1"
         second_pool = "POOL2"
         range = "192.0.2.10-192.0.2.20"
         range_config = "192.0.2.10-20"
 
         for gw in gateway:
             self.set(["gateway-address", gw])
 
         self.set(["client-ip-pool", first_pool, "range", subnet])
         self.set(["client-ip-pool", first_pool, "next-pool", second_pool])
         self.set(["client-ip-pool", second_pool, "range", range])
         self.set(["default-pool", first_pool])
         # commit changes
 
         self.cli_commit()
 
         # Validate configuration values
         conf = RawConfigParser(
             allow_no_value=True,
             delimiters="=",
             strict=False,
             dict_type=MultiOrderedDict,
         )
         conf.read(self._config_file)
 
         self.assertIn(
             f"{first_pool},next={second_pool}", conf["ip-pool"][f"{subnet},name"]
         )
         self.assertIn(second_pool, conf["ip-pool"][f"{range_config},name"])
 
         gw_pool_config_list = conf.get("ip-pool", "gw-ip-address")
         gw_ipoe_config_list = conf.get(self._protocol_section, "gw-ip-address")
         for gw in gateway:
             self.assertIn(gw.split("/")[0], gw_pool_config_list)
             self.assertIn(gw, gw_ipoe_config_list)
 
         self.assertIn(first_pool, conf[self._protocol_section]["ip-pool"])
 
     def test_accel_next_pool(self):
         self.basic_config(is_gateway=False, is_client_pool=False)
 
         first_pool = "VyOS-pool1"
         first_subnet = "192.0.2.0/25"
         first_gateway = "192.0.2.1/24"
         second_pool = "Vyos-pool2"
         second_subnet = "203.0.113.0/25"
         second_gateway = "203.0.113.1/24"
         third_pool = "Vyos-pool3"
         third_subnet = "198.51.100.0/24"
         third_gateway = "198.51.100.1/24"
 
         self.set(["gateway-address", f"{first_gateway}"])
         self.set(["gateway-address", f"{second_gateway}"])
         self.set(["gateway-address", f"{third_gateway}"])
 
         self.set(["client-ip-pool", first_pool, "range", first_subnet])
         self.set(["client-ip-pool", first_pool, "next-pool", second_pool])
         self.set(["client-ip-pool", second_pool, "range", second_subnet])
         self.set(["client-ip-pool", second_pool, "next-pool", third_pool])
         self.set(["client-ip-pool", third_pool, "range", third_subnet])
 
         # commit changes
         self.cli_commit()
 
         config = self.getConfig("ip-pool")
         # T5099 required specific order
         pool_config = f"""gw-ip-address={first_gateway.split('/')[0]}
 gw-ip-address={second_gateway.split('/')[0]}
 gw-ip-address={third_gateway.split('/')[0]}
 {third_subnet},name={third_pool}
 {second_subnet},name={second_pool},next={third_pool}
 {first_subnet},name={first_pool},next={second_pool}"""
         self.assertIn(pool_config, config)
 
     def test_accel_ipv6_pool(self):
         # Test configuration of IPv6 client pools
         self.basic_config(is_gateway=False, is_client_pool=False)
 
         pool_name = 'ipv6_test_pool'
         prefix_1 = '2001:db8:fffe::/56'
         prefix_mask = '64'
         prefix_2 = '2001:db8:ffff::/56'
         client_prefix_1 = f'{prefix_1},{prefix_mask}'
         client_prefix_2 = f'{prefix_2},{prefix_mask}'
         self.set(['client-ipv6-pool', pool_name, 'prefix', prefix_1, 'mask',
                   prefix_mask])
         self.set(['client-ipv6-pool', pool_name, 'prefix', prefix_2, 'mask',
                   prefix_mask])
 
         delegate_1_prefix = '2001:db8:fff1::/56'
         delegate_2_prefix = '2001:db8:fff2::/56'
         delegate_mask = '64'
         self.set(['client-ipv6-pool', pool_name, 'delegate', delegate_1_prefix,
                   'delegation-prefix', delegate_mask])
         self.set(['client-ipv6-pool', pool_name, 'delegate', delegate_2_prefix,
                   'delegation-prefix', delegate_mask])
 
         # commit changes
         self.cli_commit()
 
         # Validate configuration values
         conf = ConfigParser(allow_no_value=True, delimiters='=', strict=False)
         conf.read(self._config_file)
 
         for tmp in ['ipv6pool', 'ipv6_nd', 'ipv6_dhcp']:
             self.assertEqual(conf['modules'][tmp], None)
 
         config = self.getConfig("ipv6-pool")
         pool_config = f"""{client_prefix_1},name={pool_name}
 {client_prefix_2},name={pool_name}
 delegate={delegate_1_prefix},{delegate_mask},name={pool_name}
 delegate={delegate_2_prefix},{delegate_mask},name={pool_name}"""
         self.assertIn(pool_config, config)
 
+    def test_ipoe_server_vlan(self):
+        vlans = ['100', '200', '300-310']
+
+        # Test configuration of local authentication for PPPoE server
+        self.basic_config()
+        # cannot use "client-subnet" option with "vlan" option
+        # have to delete it
+        self.delete(['interface', interface, 'client-subnet'])
+        self.cli_commit()
+
+        self.set(['interface', interface, 'vlan-mon'])
+
+        # cannot use option "vlan-mon" if no "vlan" set
+        with self.assertRaises(ConfigSessionError):
+            self.cli_commit()
+
+        for vlan in vlans:
+            self.set(['interface', interface, 'vlan', vlan])
+
+        # commit changes
+        self.cli_commit()
+
+        # Validate configuration values
+        conf = ConfigParser(allow_no_value=True, delimiters='=', strict=False)
+        conf.read(self._config_file)
+        tmp = range_to_regex(vlans)
+        self.assertIn(f're:^{interface}\.{tmp}$', conf['ipoe']['interface'])
+
+        tmp = ','.join(vlans)
+        self.assertIn(f'{interface},{tmp}', conf['ipoe']['vlan-mon'])
+
     @unittest.skip("PPP is not a part of IPoE")
     def test_accel_ppp_options(self):
         pass
 
     @unittest.skip("WINS server is not used in IPoE")
     def test_accel_wins_server(self):
         pass
 
 if __name__ == "__main__":
     unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py
index 97c63d4cb..3cc1b08e0 100755
--- a/smoketest/scripts/cli/test_service_pppoe-server.py
+++ b/smoketest/scripts/cli/test_service_pppoe-server.py
@@ -1,182 +1,189 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2022-2024 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
 # published by the Free Software Foundation.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import unittest
 
 from base_accel_ppp_test import BasicAccelPPPTest
 
 from configparser import ConfigParser
 from vyos.utils.file import read_file
 from vyos.template import range_to_regex
+from vyos.configsession import ConfigSessionError
 
 local_if = ['interfaces', 'dummy', 'dum667']
 ac_name = 'ACN'
 interface = 'eth0'
 
 class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
     @classmethod
     def setUpClass(cls):
         cls._base_path = ['service', 'pppoe-server']
         cls._config_file = '/run/accel-pppd/pppoe.conf'
         cls._chap_secrets = '/run/accel-pppd/pppoe.chap-secrets'
         cls._protocol_section = 'pppoe'
         # call base-classes classmethod
         super(TestServicePPPoEServer, cls).setUpClass()
 
     def tearDown(self):
         self.cli_delete(local_if)
         super().tearDown()
 
     def verify(self, conf):
         mtu = '1492'
 
         # validate some common values in the configuration
         for tmp in ['log_syslog', 'pppoe', 'ippool',
                     'auth_mschap_v2', 'auth_mschap_v1', 'auth_chap_md5',
                     'auth_pap', 'shaper']:
             # Settings without values provide None
             self.assertEqual(conf['modules'][tmp], None)
 
         # check Access Concentrator setting
         self.assertTrue(conf['pppoe']['ac-name'] == ac_name)
         self.assertTrue(conf['pppoe'].getboolean('verbose'))
         self.assertTrue(conf['pppoe']['interface'], interface)
 
         # check ppp
         self.assertTrue(conf['ppp'].getboolean('verbose'))
         self.assertTrue(conf['ppp'].getboolean('check-ip'))
         self.assertEqual(conf['ppp']['mtu'], mtu)
 
         super().verify(conf)
 
     def basic_protocol_specific_config(self):
         self.cli_set(local_if + ['address', '192.0.2.1/32'])
         self.set(['access-concentrator', ac_name])
         self.set(['interface', interface])
 
     def test_pppoe_limits(self):
         self.basic_config()
         self.set(['limits', 'connection-limit', '20/min'])
         self.cli_commit()
         conf = ConfigParser(allow_no_value=True, delimiters='=')
         conf.read(self._config_file)
         self.assertEqual(conf['connlimit']['limit'], '20/min')
 
     def test_pppoe_server_authentication_protocols(self):
         # Test configuration of local authentication for PPPoE server
         self.basic_config()
 
         # explicitly test mschap-v2 - no special reason
         self.set( ['authentication', 'protocols', 'mschap-v2'])
 
         # commit changes
         self.cli_commit()
 
         # Validate configuration values
         conf = ConfigParser(allow_no_value=True)
         conf.read(self._config_file)
 
         self.assertEqual(conf['modules']['auth_mschap_v2'], None)
 
     def test_pppoe_server_shaper(self):
         fwmark = '223'
         limiter = 'tbf'
         self.basic_config()
 
         self.set(['shaper', 'fwmark', fwmark])
         # commit changes
 
         self.cli_commit()
 
         # Validate configuration values
         conf = ConfigParser(allow_no_value=True, delimiters='=')
         conf.read(self._config_file)
 
         # basic verification
         self.verify(conf)
 
         self.assertEqual(conf['shaper']['fwmark'], fwmark)
         self.assertEqual(conf['shaper']['down-limiter'], limiter)
 
     def test_accel_radius_authentication(self):
         radius_called_sid = 'ifname:mac'
 
         self.set(['authentication', 'radius', 'called-sid-format', radius_called_sid])
 
         # run common tests
         super().test_accel_radius_authentication()
 
         # Validate configuration values
         conf = ConfigParser(allow_no_value=True, delimiters='=')
         conf.read(self._config_file)
 
         # Validate configuration
         self.assertEqual(conf['pppoe']['called-sid'], radius_called_sid)
 
     def test_pppoe_server_vlan(self):
 
         vlans = ['100', '200', '300-310']
 
         # Test configuration of local authentication for PPPoE server
         self.basic_config()
 
+        self.set(['interface', interface, 'vlan-mon'])
+
+        # cannot use option "vlan-mon" if no "vlan" set
+        with self.assertRaises(ConfigSessionError):
+            self.cli_commit()
+
         for vlan in vlans:
             self.set(['interface', interface, 'vlan', vlan])
 
         # commit changes
         self.cli_commit()
 
         # Validate configuration values
         config = read_file(self._config_file)
         for vlan in vlans:
             tmp = range_to_regex(vlan)
             self.assertIn(f'interface=re:^{interface}\.{tmp}$', config)
 
         tmp = ','.join(vlans)
         self.assertIn(f'vlan-mon={interface},{tmp}', config)
 
     def test_pppoe_server_pado_delay(self):
         delay_without_sessions = '10'
         delays = {'20': '200', '30': '300'}
 
         self.basic_config()
 
         self.set(['pado-delay', delay_without_sessions])
         self.cli_commit()
 
         conf = ConfigParser(allow_no_value=True, delimiters='=')
         conf.read(self._config_file)
         self.assertEqual(conf['pppoe']['pado-delay'], delay_without_sessions)
 
         for delay, sessions in delays.items():
             self.set(['pado-delay', delay, 'sessions', sessions])
         self.cli_commit()
 
         conf = ConfigParser(allow_no_value=True, delimiters='=')
         conf.read(self._config_file)
 
         self.assertEqual(conf['pppoe']['pado-delay'], '10,20:200,30:300')
 
         self.set(['pado-delay', 'disable', 'sessions', '400'])
         self.cli_commit()
 
         conf = ConfigParser(allow_no_value=True, delimiters='=')
         conf.read(self._config_file)
         self.assertEqual(conf['pppoe']['pado-delay'], '10,20:200,30:300,-1:400')
 
 
 if __name__ == '__main__':
     unittest.main(verbosity=2)
diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py
index 16c82e591..c7e3ef033 100755
--- a/src/conf_mode/service_ipoe-server.py
+++ b/src/conf_mode/service_ipoe-server.py
@@ -1,114 +1,116 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-2024 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
 # published by the Free Software Foundation.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
 
 from sys import exit
 
 from vyos.config import Config
 from vyos.configdict import get_accel_dict
 from vyos.configverify import verify_interface_exists
 from vyos.template import render
 from vyos.utils.process import call
 from vyos.utils.dict import dict_search
 from vyos.accel_ppp_util import get_pools_in_order
 from vyos.accel_ppp_util import verify_accel_ppp_name_servers
 from vyos.accel_ppp_util import verify_accel_ppp_wins_servers
 from vyos.accel_ppp_util import verify_accel_ppp_ip_pool
 from vyos.accel_ppp_util import verify_accel_ppp_authentication
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 
 ipoe_conf = '/run/accel-pppd/ipoe.conf'
 ipoe_chap_secrets = '/run/accel-pppd/ipoe.chap-secrets'
 
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['service', 'ipoe-server']
     if not conf.exists(base):
         return None
 
     # retrieve common dictionary keys
     ipoe = get_accel_dict(conf, base, ipoe_chap_secrets)
 
     if dict_search('client_ip_pool', ipoe):
         # Multiple named pools require ordered values T5099
         ipoe['ordered_named_pools'] = get_pools_in_order(dict_search('client_ip_pool', ipoe))
 
     ipoe['server_type'] = 'ipoe'
     return ipoe
 
 
 def verify(ipoe):
     if not ipoe:
         return None
 
     if 'interface' not in ipoe:
         raise ConfigError('No IPoE interface configured')
 
     for interface, iface_config in ipoe['interface'].items():
         verify_interface_exists(ipoe, interface, warning_only=True)
         if 'client_subnet' in iface_config and 'vlan' in iface_config:
             raise ConfigError('Option "client-subnet" and "vlan" are mutually exclusive, '
                               'use "client-ip-pool" instead!')
+        if 'vlan_mon' in iface_config and not 'vlan' in iface_config:
+            raise ConfigError('Option "vlan-mon" requires "vlan" to be set!')
 
     verify_accel_ppp_authentication(ipoe, local_users=False)
     verify_accel_ppp_ip_pool(ipoe)
     verify_accel_ppp_name_servers(ipoe)
     verify_accel_ppp_wins_servers(ipoe)
 
     return None
 
 
 def generate(ipoe):
     if not ipoe:
         return None
 
     render(ipoe_conf, 'accel-ppp/ipoe.config.j2', ipoe)
 
     if dict_search('authentication.mode', ipoe) == 'local':
         render(ipoe_chap_secrets, 'accel-ppp/chap-secrets.ipoe.j2',
                ipoe, permission=0o640)
     return None
 
 
 def apply(ipoe):
     systemd_service = 'accel-ppp@ipoe.service'
     if ipoe == None:
         call(f'systemctl stop {systemd_service}')
         for file in [ipoe_conf, ipoe_chap_secrets]:
             if os.path.exists(file):
                 os.unlink(file)
 
         return None
 
     call(f'systemctl reload-or-restart {systemd_service}')
 
 if __name__ == '__main__':
     try:
         c = get_config()
         verify(c)
         generate(c)
         apply(c)
     except ConfigError as e:
         print(e)
         exit(1)
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index 566a7b149..ac697c509 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -1,164 +1,167 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-2024 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
 # published by the Free Software Foundation.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
 
 from sys import exit
 
 from vyos.config import Config
 from vyos.configdict import get_accel_dict
 from vyos.configdict import is_node_changed
 from vyos.configverify import verify_interface_exists
 from vyos.template import render
 from vyos.utils.process import call
 from vyos.utils.dict import dict_search
 from vyos.accel_ppp_util import verify_accel_ppp_name_servers
 from vyos.accel_ppp_util import verify_accel_ppp_wins_servers
 from vyos.accel_ppp_util import verify_accel_ppp_authentication
 from vyos.accel_ppp_util import verify_accel_ppp_ip_pool
 from vyos.accel_ppp_util import get_pools_in_order
 from vyos import ConfigError
 from vyos import airbag
 
 airbag.enable()
 
 pppoe_conf = r'/run/accel-pppd/pppoe.conf'
 pppoe_chap_secrets = r'/run/accel-pppd/pppoe.chap-secrets'
 
 def convert_pado_delay(pado_delay):
     new_pado_delay = {'delays_without_sessions': [],
                       'delays_with_sessions': []}
     for delay, sessions in pado_delay.items():
         if not sessions:
             new_pado_delay['delays_without_sessions'].append(delay)
         else:
             new_pado_delay['delays_with_sessions'].append((delay, int(sessions['sessions'])))
     return new_pado_delay
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['service', 'pppoe-server']
     if not conf.exists(base):
         return None
 
     # retrieve common dictionary keys
     pppoe = get_accel_dict(conf, base, pppoe_chap_secrets)
 
     if dict_search('client_ip_pool', pppoe):
         # Multiple named pools require ordered values T5099
         pppoe['ordered_named_pools'] = get_pools_in_order(dict_search('client_ip_pool', pppoe))
 
     if dict_search('pado_delay', pppoe):
         pado_delay = dict_search('pado_delay', pppoe)
         pppoe['pado_delay'] = convert_pado_delay(pado_delay)
 
     # reload-or-restart does not implemented in accel-ppp
     # use this workaround until it will be implemented
     # https://phabricator.accel-ppp.org/T3
     conditions = [is_node_changed(conf, base + ['client-ip-pool']),
                   is_node_changed(conf, base + ['client-ipv6-pool']),
                   is_node_changed(conf, base + ['interface'])]
     if any(conditions):
         pppoe.update({'restart_required': {}})
     pppoe['server_type'] = 'pppoe'
     return pppoe
 
 def verify_pado_delay(pppoe):
     if 'pado_delay' in pppoe:
         pado_delay = pppoe['pado_delay']
 
         delays_without_sessions = pado_delay['delays_without_sessions']
         if 'disable' in delays_without_sessions:
             raise ConfigError(
                 'Number of sessions must be specified for "pado-delay disable"'
             )
 
         if len(delays_without_sessions) > 1:
             raise ConfigError(
                 f'Cannot add more then ONE pado-delay without sessions, '
                 f'but {len(delays_without_sessions)} were set'
             )
 
         if 'disable' in [delay[0] for delay in pado_delay['delays_with_sessions']]:
             # need to sort delays by sessions to verify if there is no delay
             # for sessions after disabling
             sorted_pado_delay = sorted(pado_delay['delays_with_sessions'], key=lambda k_v: k_v[1])
             last_delay = sorted_pado_delay[-1]
 
             if last_delay[0] != 'disable':
                 raise ConfigError(
                     f'Cannot add pado-delay after disabled sessions, but '
                     f'"pado-delay {last_delay[0]} sessions {last_delay[1]}" was set'
                 )
 
 def verify(pppoe):
     if not pppoe:
         return None
 
     verify_accel_ppp_authentication(pppoe)
     verify_accel_ppp_ip_pool(pppoe)
     verify_accel_ppp_name_servers(pppoe)
     verify_accel_ppp_wins_servers(pppoe)
     verify_pado_delay(pppoe)
 
     if 'interface' not in pppoe:
         raise ConfigError('At least one listen interface must be defined!')
 
     # Check is interface exists in the system
-    for interface in pppoe['interface']:
+    for interface, interface_config in pppoe['interface'].items():
         verify_interface_exists(pppoe, interface, warning_only=True)
 
+        if 'vlan_mon' in interface_config and not 'vlan' in interface_config:
+            raise ConfigError('Option "vlan-mon" requires "vlan" to be set!')
+
     return None
 
 
 def generate(pppoe):
     if not pppoe:
         return None
 
     render(pppoe_conf, 'accel-ppp/pppoe.config.j2', pppoe)
 
     if dict_search('authentication.mode', pppoe) == 'local':
         render(pppoe_chap_secrets, 'accel-ppp/chap-secrets.config_dict.j2',
                pppoe, permission=0o640)
     return None
 
 
 def apply(pppoe):
     systemd_service = 'accel-ppp@pppoe.service'
     if not pppoe:
         call(f'systemctl stop {systemd_service}')
         for file in [pppoe_conf, pppoe_chap_secrets]:
             if os.path.exists(file):
                 os.unlink(file)
         return None
 
     if 'restart_required' in pppoe:
         call(f'systemctl restart {systemd_service}')
     else:
         call(f'systemctl reload-or-restart {systemd_service}')
 
 if __name__ == '__main__':
     try:
         c = get_config()
         verify(c)
         generate(c)
         apply(c)
     except ConfigError as e:
         print(e)
         exit(1)
diff --git a/src/migration-scripts/ipoe-server/3-to-4 b/src/migration-scripts/ipoe-server/3-to-4
new file mode 100644
index 000000000..3bad9756d
--- /dev/null
+++ b/src/migration-scripts/ipoe-server/3-to-4
@@ -0,0 +1,30 @@
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library.  If not, see <http://www.gnu.org/licenses/>.
+
+# Add the "vlan-mon" option to the configuration to prevent it
+# from disappearing from the configuration file
+
+from vyos.configtree import ConfigTree
+
+base = ['service', 'ipoe-server']
+
+def migrate(config: ConfigTree) -> None:
+    if not config.exists(base):
+        return
+
+    for interface in config.list_nodes(base + ['interface']):
+        base_path = base + ['interface', interface]
+        if config.exists(base_path + ['vlan']):
+            config.set(base_path + ['vlan-mon'])
diff --git a/src/migration-scripts/pppoe-server/10-to-11 b/src/migration-scripts/pppoe-server/10-to-11
new file mode 100644
index 000000000..6bc138b5c
--- /dev/null
+++ b/src/migration-scripts/pppoe-server/10-to-11
@@ -0,0 +1,30 @@
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library.  If not, see <http://www.gnu.org/licenses/>.
+
+# Add the "vlan-mon" option to the configuration to prevent it
+# from disappearing from the configuration file
+
+from vyos.configtree import ConfigTree
+
+base = ['service', 'pppoe-server']
+
+def migrate(config: ConfigTree) -> None:
+    if not config.exists(base):
+        return
+
+    for interface in config.list_nodes(base + ['interface']):
+        base_path = base + ['interface', interface]
+        if config.exists(base_path + ['vlan']):
+            config.set(base_path + ['vlan-mon'])