diff --git a/docs/site-example/site.conf b/docs/site-example/site.conf
index 6cdcd743cf415e89e5357450b32a03f66992dbbe..3b17876e33ff499e3de1b873e3c92c0cde2e9342 100644
--- a/docs/site-example/site.conf
+++ b/docs/site-example/site.conf
@@ -79,6 +79,10 @@
     },
   },
 
+  mesh = {
+    vxlan = true,
+  },
+
   -- The next node feature allows clients to always reach the node it is
   -- connected to using a known IP address.
   next_node = {
diff --git a/docs/user/site.rst b/docs/user/site.rst
index bfceb83e5696f5bdfcf6dd16548f420d9e4a1f1a..a8daf55f8117546de6d265681185bc82b2f53c29 100644
--- a/docs/user/site.rst
+++ b/docs/user/site.rst
@@ -182,9 +182,16 @@ next_node \: package
     in isolated mesh segments). This is possible by providing one or more names
     in the ``name`` field.
 
-mesh \: optional
+mesh
     Configuration of general mesh functionality.
 
+    To avoid inter-mesh links, Gluon can encapsulate the mesh protocol in VXLAN
+    for Mesh-on-LAN/WAN. It is recommended to set *mesh.vxlan* to ``true`` to
+    enable VXLAN in new setups. Setting it to ``false`` disables this
+    encapsulation to allow meshing with other nodes that don't support VXLAN
+    (Gluon 2017.1.x and older). In multi-domain setups, *mesh.vxlan* is optional
+    and defaults to ``true``.
+
     Gluon generally segments layer-2 meshes so that each node becomes IGMP/MLD
     querier for its own local clients. This is necessary for reliable multicast
     snooping. The segmentation is realized by preventing IGMP/MLD queries from
@@ -197,7 +204,7 @@ mesh \: optional
     the mesh; this is usually not a problem, as such setups are unusual. If
     you run a special-purpose mesh that requires membership reports to be
     working, this filtering can be disabled by setting the
-    *filter_membership_reports* option to ``false``.
+    optional *filter_membership_reports* value to ``false``.
 
     In addition, options specific to the batman-adv routing protocol can be set
     in the *batman_adv* section:
@@ -208,6 +215,7 @@ mesh \: optional
     ::
 
       mesh = {
+        vxlan = true,
         filter_membership_reports = false,
         batman_adv = {
           gw_sel_class = 1,
diff --git a/package/gluon-core/check_site.lua b/package/gluon-core/check_site.lua
index 06840eb0cc0ca466451364b96b74e92741e932f1..0977b08690e799466fae62193371dcfa3fb4c1e3 100644
--- a/package/gluon-core/check_site.lua
+++ b/package/gluon-core/check_site.lua
@@ -73,6 +73,8 @@ need_string_array(in_domain({'next_node', 'name'}), false)
 need_string_match(in_domain({'next_node', 'ip6'}), '^[%x:]+$', false)
 need_string_match(in_domain({'next_node', 'ip4'}), '^%d+.%d+.%d+.%d+$', false)
 
+need_boolean(in_domain({'mesh', 'vxlan'}), false)
+
 need_boolean(in_site({'mesh_on_wan'}), false)
 need_boolean(in_site({'mesh_on_lan'}), false)
 need_boolean(in_site({'single_as_lan'}), false)
diff --git a/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh b/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh
index cdbb45bb73ea21d97f62def1c789cf8da684cb75..4a44a30eaa444ce12d1c65048569b93d71d12033 100755
--- a/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh
+++ b/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh
@@ -7,7 +7,6 @@ init_proto "$@"
 proto_gluon_wired_init_config() {
         proto_config_add_boolean transitive
         proto_config_add_int index
-        proto_config_add_boolean legacy
 }
 
 xor2() {
@@ -28,13 +27,15 @@ proto_gluon_wired_setup() {
 
         local meshif="$config"
 
-        local transitive index legacy
-        json_get_vars transitive index legacy
+        local vxlan="$(lua -e 'print(require("gluon.site").mesh.vxlan(true))')"
+
+        local transitive index
+        json_get_vars transitive index
 
         proto_init_update "$ifname" 1
         proto_send_update "$config"
 
-        if [ "${legacy:-0}" -eq 0 ]; then
+        if [ "$vxlan" = 'true' ]; then
                 meshif="vx_$config"
 
                 json_init
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-wan b/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-wan
index 060a2d4e79a5b8914e01454d9c4dc6f39e7bf166..e8600ee864afc6cf135950c12b2bbb1bdbc7c6bc 100755
--- a/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-wan
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-wan
@@ -22,11 +22,9 @@ uci:set('network', 'mesh_wan', 'disabled', not enable)
 if uci:get('network', 'mesh_wan', 'transitive') == nil then
 	uci:set('network', 'mesh_wan', 'transitive', true)
 end
-if uci:get('network', 'mesh_wan', 'legacy') == nil then
-	uci:set('network', 'mesh_wan', 'legacy', old_proto == 'gluon_mesh')
-end
 
 uci:delete('network', 'mesh_wan', 'auto')
 uci:delete('network', 'mesh_wan', 'fixed_mtu')
+uci:delete('network', 'mesh_wan', 'legacy')
 
 uci:save('network')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/220-interface-lan b/package/gluon-core/luasrc/lib/gluon/upgrade/220-interface-lan
index 365a73b99fb81e5d3d2d1bbf122d45b9dcc9dae5..a6fb41276d6929ead6f0cdce87b60354c96ffe1f 100755
--- a/package/gluon-core/luasrc/lib/gluon/upgrade/220-interface-lan
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/220-interface-lan
@@ -50,11 +50,9 @@ uci:set('network', 'mesh_lan', 'disabled', not enable)
 if uci:get('network', 'mesh_lan', 'transitive') == nil then
 	uci:set('network', 'mesh_lan', 'transitive', true)
 end
-if uci:get('network', 'mesh_lan', 'legacy') == nil then
-	uci:set('network', 'mesh_lan', 'legacy', old_proto == 'gluon_mesh')
-end
 
 uci:delete('network', 'mesh_lan', 'auto')
 uci:delete('network', 'mesh_lan', 'fixed_mtu')
+uci:delete('network', 'mesh_lan', 'legacy')
 
 uci:save('network')
diff --git a/package/gluon-mesh-batman-adv/check_site.lua b/package/gluon-mesh-batman-adv/check_site.lua
index cde9478c3b80e543d9a1d0d6380c6e0ef5edb669..f5ea9fb4a7fe946a467bb6bd8514966b3a502755 100644
--- a/package/gluon-mesh-batman-adv/check_site.lua
+++ b/package/gluon-mesh-batman-adv/check_site.lua
@@ -1,2 +1,5 @@
+-- mesh/vxlan is required in single domain setups (this_domain() is nil)
+need_boolean(in_domain({'mesh', 'vxlan'}), not this_domain())
+
 need_number({'mesh', 'batman_adv', 'gw_sel_class'}, false)
 need_one_of({'mesh', 'batman_adv', 'routing_algo'}, {'BATMAN_IV', 'BATMAN_V'}, false)