diff --git a/ff-mesh-vpn-tunneldigger/Makefile b/ff-mesh-vpn-tunneldigger/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..f421a51278ac4b63798f4a0ea3fa4ac76cd2cd0a
--- /dev/null
+++ b/ff-mesh-vpn-tunneldigger/Makefile
@@ -0,0 +1,16 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=ff-mesh-vpn-tunneldigger
+PKG_VERSION:=0.0.1
+PKG_RELEASE:=1
+
+PKG_LICENSE:=BSD-2-Clause
+
+include $(TOPDIR)/../package/gluon.mk
+
+define Package/$(PKG_NAME)
+  TITLE:=Support for connecting meshes via tunneldigger/L2TPv3 pseudowire
+  DEPENDS:=+gluon-core +gluon-mesh-vpn-core +tunneldigger +simple-tc
+endef
+
+$(eval $(call BuildPackageGluon,$(PKG_NAME)))
diff --git a/ff-mesh-vpn-tunneldigger/README.md b/ff-mesh-vpn-tunneldigger/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..50a0f4ef6d1cdb9a161a0136b2e6219e5a7d1182
--- /dev/null
+++ b/ff-mesh-vpn-tunneldigger/README.md
@@ -0,0 +1,13 @@
+# ff-mesh-vpn-tunneldigger
+
+This package is based on the former core gluon package [gluon-mesh-vpn-tunneldigger](https://github.com/freifunk-gluon/gluon/tree/c2dc338abfbebb34dcf62124dc09be85fa88f8ef/package/gluon-mesh-vpn-tunneldigger).
+
+It you want to keep using tunneldigger you need to take the following steps:
+
+- `modules`: add this repo as described in the [README.md](../README.md#using-this-repository)
+- `image-customization.lua`: remove the `mesh-vpn-tunneldigger` feature
+- `image-customization.lua`: add the `config-mode-mesh-vpn` feature:  
+  `features({'config-mode-mesh-vpn'})`  
+  Not needed if you don't use the config mode or don't want to enable configuration of VPN settings via the config mode.
+- `image-customization.lua`: add the `ff-mesh-vpn-tunneldigger` package:  
+  `packages({'ff-mesh-vpn-tunneldigger'})`
diff --git a/ff-mesh-vpn-tunneldigger/check_site.lua b/ff-mesh-vpn-tunneldigger/check_site.lua
new file mode 100644
index 0000000000000000000000000000000000000000..77ea83c5cf304de82ce26d1b0fe6b1387256d2c4
--- /dev/null
+++ b/ff-mesh-vpn-tunneldigger/check_site.lua
@@ -0,0 +1,2 @@
+need_string_array(in_domain({'mesh_vpn', 'tunneldigger', 'brokers'}))
+need_number({'mesh_vpn', 'tunneldigger', 'mtu'})
diff --git a/ff-mesh-vpn-tunneldigger/files/lib/gluon/mesh-vpn/provider/tunneldigger b/ff-mesh-vpn-tunneldigger/files/lib/gluon/mesh-vpn/provider/tunneldigger
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/ff-mesh-vpn-tunneldigger/files/lib/gluon/reload.d/310-tunneldigger-stop b/ff-mesh-vpn-tunneldigger/files/lib/gluon/reload.d/310-tunneldigger-stop
new file mode 100755
index 0000000000000000000000000000000000000000..651530b260410c35d95b03ee59eecaa8c61f7e83
--- /dev/null
+++ b/ff-mesh-vpn-tunneldigger/files/lib/gluon/reload.d/310-tunneldigger-stop
@@ -0,0 +1,2 @@
+#!/bin/sh
+/etc/init.d/tunneldigger stop
diff --git a/ff-mesh-vpn-tunneldigger/files/lib/gluon/reload.d/790-tunneldigger-start b/ff-mesh-vpn-tunneldigger/files/lib/gluon/reload.d/790-tunneldigger-start
new file mode 100755
index 0000000000000000000000000000000000000000..9b174f178b530c6e759603e2ca62ff8c84955503
--- /dev/null
+++ b/ff-mesh-vpn-tunneldigger/files/lib/gluon/reload.d/790-tunneldigger-start
@@ -0,0 +1,2 @@
+#!/bin/sh
+/etc/init.d/tunneldigger start
diff --git a/ff-mesh-vpn-tunneldigger/files/usr/lib/micron.d/tunneldigger-watchdog b/ff-mesh-vpn-tunneldigger/files/usr/lib/micron.d/tunneldigger-watchdog
new file mode 100644
index 0000000000000000000000000000000000000000..c4ae3bc6c7b50c8eba3083d280da2030cd7f41d3
--- /dev/null
+++ b/ff-mesh-vpn-tunneldigger/files/usr/lib/micron.d/tunneldigger-watchdog
@@ -0,0 +1 @@
+*/5 * * * * /usr/bin/tunneldigger-watchdog
diff --git a/ff-mesh-vpn-tunneldigger/luasrc/lib/gluon/upgrade/400-mesh-vpn-tunneldigger b/ff-mesh-vpn-tunneldigger/luasrc/lib/gluon/upgrade/400-mesh-vpn-tunneldigger
new file mode 100755
index 0000000000000000000000000000000000000000..9888d87eb5c0b01c7e4e9e43034d68c1bc82b972
--- /dev/null
+++ b/ff-mesh-vpn-tunneldigger/luasrc/lib/gluon/upgrade/400-mesh-vpn-tunneldigger
@@ -0,0 +1,19 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site'
+local util = require 'gluon.util'
+local vpn_core = require 'gluon.mesh-vpn'
+
+local uci = require('simple-uci').cursor()
+
+
+uci:section('tunneldigger', 'broker', 'mesh_vpn', {
+	uuid = util.node_id(),
+	interface = vpn_core.get_interface(),
+	bind_interface = 'br-wan',
+	group = 'gluon-mesh-vpn',
+	broker_selection = 'usage',
+	address = site.mesh_vpn.tunneldigger.brokers(),
+})
+
+uci:save('tunneldigger')
diff --git a/ff-mesh-vpn-tunneldigger/luasrc/usr/bin/tunneldigger-watchdog b/ff-mesh-vpn-tunneldigger/luasrc/usr/bin/tunneldigger-watchdog
new file mode 100755
index 0000000000000000000000000000000000000000..0f1e5603c13d06fe89eecdf055f641793bbd841c
--- /dev/null
+++ b/ff-mesh-vpn-tunneldigger/luasrc/usr/bin/tunneldigger-watchdog
@@ -0,0 +1,31 @@
+#!/usr/bin/lua
+
+local uci = require('simple-uci').cursor()
+
+local function restart_tunneldigger()
+	os.execute('logger -t tunneldigger-watchdog "Restarting Tunneldigger."')
+	os.execute('/etc/init.d/tunneldigger restart')
+end
+
+local function has_mesh_vpn_neighbours()
+	local handle = io.popen('batctl o', 'r')
+	if not handle then
+		return false
+	end
+	for line in handle:lines() do
+		if line:find('mesh%-vpn') then
+			handle:close()
+			return true
+		end
+	end
+	handle:close()
+	return false
+end
+
+if uci:get_bool('tunneldigger', 'mesh_vpn', 'enabled') then
+	if not has_mesh_vpn_neighbours() then
+		os.execute('logger -t tunneldigger-watchdog "No vpn-mesh neighbours found."')
+		restart_tunneldigger()
+		return
+	end
+end
diff --git a/ff-mesh-vpn-tunneldigger/luasrc/usr/lib/lua/gluon/mesh-vpn/provider/tunneldigger.lua b/ff-mesh-vpn-tunneldigger/luasrc/usr/lib/lua/gluon/mesh-vpn/provider/tunneldigger.lua
new file mode 100644
index 0000000000000000000000000000000000000000..9ae67539ddfd01ba89692c8a1f424e78fb5b03df
--- /dev/null
+++ b/ff-mesh-vpn-tunneldigger/luasrc/usr/lib/lua/gluon/mesh-vpn/provider/tunneldigger.lua
@@ -0,0 +1,46 @@
+local uci = require('simple-uci').cursor()
+
+local site = require 'gluon.site'
+local vpn_core = require 'gluon.mesh-vpn'
+
+local M = {}
+
+function M.public_key()
+	return nil
+end
+
+function M.enable(val)
+	uci:set('tunneldigger', 'mesh_vpn', 'enabled', val)
+	uci:save('tunneldigger')
+end
+
+function M.active()
+	return site.mesh_vpn.tunneldigger() ~= nil
+end
+
+function M.set_limit(ingress_limit, egress_limit)
+	if ingress_limit ~= nil then
+		uci:set('tunneldigger', 'mesh_vpn', 'limit_bw_down', ingress_limit)
+	else
+		uci:delete('tunneldigger', 'mesh_vpn', 'limit_bw_down')
+	end
+
+	if egress_limit ~= nil then
+		uci:section('simple-tc', 'interface', 'mesh_vpn', {
+			ifname = vpn_core.get_interface(),
+			enabled = true,
+			limit_egress = egress_limit,
+		})
+	else
+		uci:delete('simple-tc', 'mesh_vpn')
+	end
+
+	uci:save('tunneldigger')
+	uci:save('simple-tc')
+end
+
+function M.mtu()
+	return site.mesh_vpn.tunneldigger.mtu()
+end
+
+return M