diff --git a/docs/site-example/site.conf b/docs/site-example/site.conf
index 2df60661ef6148178a2cc58728d62691b6165c6e..e518ff9e4a593664f7ce0027b3130be398641b31 100644
--- a/docs/site-example/site.conf
+++ b/docs/site-example/site.conf
@@ -61,6 +61,7 @@
   -- for channel.
   wifi5 = {
     channel = 44,
+    outdoor_chanlist = '100-140',
     ap = {
       ssid = 'alpha-centauri.freifunk.net',
     },
diff --git a/docs/user/site.rst b/docs/user/site.rst
index 4f4e9d45306d3af3a026b5e56dafbb2c8b9293af..b1549b23b999941d2000e76c05134227e62dd8f0 100644
--- a/docs/user/site.rst
+++ b/docs/user/site.rst
@@ -166,6 +166,25 @@ wifi24 \: optional
 wifi5 \: optional
     Same as `wifi24` but for the 5Ghz radio.
 
+    Additionally a range of channels that are safe to use outsides on the 5 GHz band can
+    be set up through ``outdoor_chanlist``, which allows for a space-seperated list of
+    channels and channel ranges, seperated by a hyphen.
+    When set this offers the outdoor mode flag for 5 GHz radios in the config mode which
+    reconfigures the AP to select its channel from outdoor chanlist, while respecting
+    regulatory specifications, and  disables mesh on that radio.
+    The ``outdoors`` option in turn allows to configure when outdoor mode will be enabled.
+    When set to ``true`` all 5 GHz radios will use outdoor channels, while on ``false``
+    the outdoor mode will be completely disabled. The default setting is ``'preset'``,
+    which will enable outdoor mode automatically on outdoor-capable devices.
+    ::
+
+      wifi5 = {
+        channel = 44,
+        outdoor_chanlist = "100-140",
+
+        [...]
+      },
+
 next_node \: package
     Configuration of the local node feature of Gluon
     ::
diff --git a/package/features b/package/features
index 8b44c82e12bd2d3a891a67f8be97474ec875167a..5de9e424d7edcec6757ea1c68cf59c5a8621c442 100644
--- a/package/features
+++ b/package/features
@@ -3,7 +3,8 @@ nodefault 'web-wizard'
 packages 'web-wizard' \
 	'gluon-config-mode-hostname' \
 	'gluon-config-mode-geo-location' \
-	'gluon-config-mode-contact-info'
+	'gluon-config-mode-contact-info' \
+	'gluon-config-mode-outdoor'
 
 packages 'web-wizard & autoupdater' \
 	'gluon-config-mode-autoupdater'
diff --git a/package/gluon-config-mode-outdoor/Makefile b/package/gluon-config-mode-outdoor/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2f3acfcd1695df43769d6559f26312a746d0096d
--- /dev/null
+++ b/package/gluon-config-mode-outdoor/Makefile
@@ -0,0 +1,13 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-config-mode-outdoor
+PKG_VERSION:=1
+
+include ../gluon.mk
+
+define Package/gluon-config-mode-outdoor
+  TITLE:=UI for displaying & changing the outdoor mode flag in the wizard
+  DEPENDS:=+gluon-config-mode-core
+endef
+
+$(eval $(call BuildPackageGluon,gluon-config-mode-outdoor))
diff --git a/package/gluon-config-mode-outdoor/i18n/de.po b/package/gluon-config-mode-outdoor/i18n/de.po
new file mode 100644
index 0000000000000000000000000000000000000000..55cdb230a6cfa5ae002b291d4d705d973fc61455
--- /dev/null
+++ b/package/gluon-config-mode-outdoor/i18n/de.po
@@ -0,0 +1,9 @@
+msgid ""
+"Please enable this option in case the node is to be installed outdoors "
+"to comply with local frequency regulations."
+msgstr ""
+"Wenn der Knoten im Freien aufgestellt werden soll, dann aktiviere bitte "
+"diese Option um den örtlichen Frequenzbestimmungen zu entsprechen."
+
+msgid "Node will be installed outdoors"
+msgstr "Knoten wird im Außenbereich betrieben"
diff --git a/package/gluon-config-mode-outdoor/i18n/gluon-config-mode-outdoor.pot b/package/gluon-config-mode-outdoor/i18n/gluon-config-mode-outdoor.pot
new file mode 100644
index 0000000000000000000000000000000000000000..d7c04908a9a54f311a47a54981faa61dbe0a2b08
--- /dev/null
+++ b/package/gluon-config-mode-outdoor/i18n/gluon-config-mode-outdoor.pot
@@ -0,0 +1,7 @@
+msgid ""
+"Please enable this option in case the node is to be installed outdoors "
+"to comply with local frequency regulations."
+msgstr ""
+
+msgid "Node will be installed outdoors"
+msgstr ""
diff --git a/package/gluon-config-mode-outdoor/luasrc/lib/gluon/config-mode/wizard/0250-outdoor.lua b/package/gluon-config-mode-outdoor/luasrc/lib/gluon/config-mode/wizard/0250-outdoor.lua
new file mode 100644
index 0000000000000000000000000000000000000000..edfe839f75653a00cd86619334c9b55d5eb97de3
--- /dev/null
+++ b/package/gluon-config-mode-outdoor/luasrc/lib/gluon/config-mode/wizard/0250-outdoor.lua
@@ -0,0 +1,28 @@
+return function(form, uci)
+        local platform_info = require 'platform_info'
+
+	if not platform_info.is_outdoor_device() then
+		-- only visible on wizard for outdoor devices
+		return
+	end
+
+	local pkg_i18n = i18n 'gluon-config-mode-outdoor'
+
+	local section = form:section(Section, nil, pkg_i18n.translate(
+		"Please enable this option in case the node is to be installed outdoors "
+		.. "to comply with local frequency regulations."
+	))
+
+	local outdoor = section:option(Flag, 'outdoor', pkg_i18n.translate("Node will be installed outdoors"))
+	outdoor.default = outdoor_mode
+
+	function outdoor:write(data)
+		if data ~= outdoor_mode then
+			uci:set('gluon', 'wireless', 'outdoor', data)
+			uci:save('gluon')
+			os.execute('/lib/gluon/upgrade/200-wireless')
+		end
+	end
+
+	return {'gluon', 'wireless'}
+end
diff --git a/package/gluon-core/check_site.lua b/package/gluon-core/check_site.lua
index 51791e0b6b469a26a3a4be9c7672b5ec147ef22f..74a6c3f9a3252ec19b569555babf9f60f8e6019f 100644
--- a/package/gluon-core/check_site.lua
+++ b/package/gluon-core/check_site.lua
@@ -33,7 +33,19 @@ for _, config in ipairs({'wifi24', 'wifi5'}) do
 	if need_table({config}, nil, false) then
 		need_string(in_site({'regdom'})) -- regdom is only required when wifi24 or wifi5 is configured
 
-		need_number({config, 'channel'})
+		if config == "wifi24" then
+			local channels = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
+			need_one_of({config, 'channel'}, channels)
+		elseif config == 'wifi5' then
+			local channels = {
+				34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
+				64, 96, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 
+				120, 122, 124, 126, 128, 132, 134, 136, 138, 140, 142, 144, 
+				149, 151, 153, 155, 157, 159, 161, 165, 169, 173 }
+			need_one_of({config, 'channel'}, channels)
+			need_chanlist({config, 'outdoor_chanlist'}, channels, false)
+			need_one_of({config, 'outdoors'}, {true, false, 'preset'}, false)
+		end
 
 		obsolete({config, 'supported_rates'}, '802.11b rates are disabled by default.')
 		obsolete({config, 'basic_rate'}, '802.11b rates are disabled by default.')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/180-outdoors b/package/gluon-core/luasrc/lib/gluon/upgrade/180-outdoors
new file mode 100755
index 0000000000000000000000000000000000000000..3acaa63c93d973202193eb40229fd82e70deec5e
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/180-outdoors
@@ -0,0 +1,35 @@
+#!/usr/bin/lua
+
+-- This script needs to be sorted before 200-wireless as it affects
+-- wireless channel selection and wireless mesh configuration.
+
+local uci = require('simple-uci').cursor()
+local site = require 'gluon.site'
+
+if uci:get('gluon', 'wireless', 'outdoor') ~= nil then
+	-- don't overwrite existing configuration
+	os.exit(0)
+end
+
+local sysconfig = require 'gluon.sysconfig'
+local platform_info = require 'platform_info'
+
+local config = site.wifi5.outdoor_preset('preset')
+local outdoor = false
+
+if sysconfig.gluon_version then
+	-- don't enable the outdoor mode after an upgrade
+	outdoor = false
+elseif config == 'preset' then
+	-- enable outdoor mode through presets on new installs
+	outdoor = platform_info.is_outdoor_device()
+else
+	-- enable/disable outdoor mode unconditionally on new installs
+	outdoor = config
+end
+
+uci:section('gluon', 'wireless', 'wireless', {
+	outdoor = outdoor
+})
+
+uci:save('gluon')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/200-wireless b/package/gluon-core/luasrc/lib/gluon/upgrade/200-wireless
index adc1b17ca819e4ce0e779597ff9560c6284a98d0..ccf485455d64896e79b57220555d25cf9fe009b9 100755
--- a/package/gluon-core/luasrc/lib/gluon/upgrade/200-wireless
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/200-wireless
@@ -49,22 +49,37 @@ if not sysconfig.gluon_version then
 	end)
 end
 
+local function is_outdoor()
+	return uci:get_bool('gluon', 'wireless', 'outdoor')
+end
+
 local function get_channel(radio, config)
 	local channel
 	if uci:get_first('gluon-core', 'wireless', 'preserve_channels') then
+		-- preserved channel always wins
 		channel = radio.channel
+	elseif  (radio.hwmode == '11a' or radio.hwmode == '11na') and is_outdoor() then
+		-- actual channel will be picked and probed from chanlist
+		channel = 'auto'
 	end
 
 	return channel or config.channel()
 end
 
 local function get_htmode(radio)
-       local phy = util.find_phy(radio)
-       if iwinfo.nl80211.hwmodelist(phy).ac then
-               return 'VHT20'
-       else
-               return 'HT20'
-       end
+	if (radio.hwmode == '11a' or radio.hwmode == '11na') and is_outdoor() then
+		local outdoor_htmode = uci:get('gluon', 'wireless', 'outdoor_' .. radio['.name'] .. '_htmode')
+		if outdoor_htmode ~= nil then
+			return outdoor_htmode
+		end
+	end
+
+	local phy = util.find_phy(radio)
+	if iwinfo.nl80211.hwmodelist(phy).ac then
+		return 'VHT20'
+	end
+
+	return 'HT20'
 end
 
 local function is_disabled(name)
@@ -207,32 +222,50 @@ util.foreach_radio(uci, function(radio, index, config)
 	uci:set('wireless', radio_name, 'htmode', htmode)
 	uci:set('wireless', radio_name, 'country', site.regdom())
 
+	uci:delete('wireless', radio_name, 'supported_rates')
+	uci:delete('wireless', radio_name, 'basic_rate')
+
 	local hwmode = radio.hwmode
 	if hwmode == '11g' or hwmode == '11ng' then
 		uci:set('wireless', radio_name, 'legacy_rates', false)
+	elseif (hwmode == '11a' or hwmode == '11na') then
+		if is_outdoor() then
+			uci:set('wireless', radio_name, 'channels', config.outdoor_chanlist())
+
+			-- enforce outdoor channels by filtering the regdom for outdoor channels
+			local hostapd_options = uci:get_list('wireless', radio_name, 'hostapd_options')
+			util.add_to_set(hostapd_options, 'country3=0x4f')
+			uci:set_list('wireless', radio_name, 'hostapd_options', hostapd_options)
+
+			uci:delete('wireless', 'ibss_' .. radio_name)
+			uci:delete('wireless', 'mesh_' .. radio_name)
+		else
+			uci:delete('wireless', radio_name, 'channels')
+
+			local hostapd_options = uci:get_list('wireless', radio_name, 'hostapd_options')
+			util.remove_from_set(hostapd_options, 'country3=0x4f')
+			uci:set_list('wireless', radio_name, 'hostapd_options', hostapd_options)
+
+			local ibss_disabled = is_disabled('ibss_' .. radio_name)
+			local mesh_disabled = is_disabled('mesh_' .. radio_name)
+
+			configure_ibss(config.ibss(), radio, index, suffix,
+				first_non_nil(
+					ibss_disabled,
+					mesh_disabled,
+					config.ibss.disabled(false)
+				)
+			)
+			configure_mesh(config.mesh(), radio, index, suffix,
+				first_non_nil(
+					mesh_disabled,
+					ibss_disabled,
+					config.mesh.disabled(false)
+				)
+			)
+		end
 	end
 
-	uci:delete('wireless', radio_name, 'supported_rates')
-	uci:delete('wireless', radio_name, 'basic_rate')
-
-	local ibss_disabled = is_disabled('ibss_' .. radio_name)
-	local mesh_disabled = is_disabled('mesh_' .. radio_name)
-
-	configure_ibss(config.ibss(), radio, index, suffix,
-		first_non_nil(
-			ibss_disabled,
-			mesh_disabled,
-			config.ibss.disabled(false)
-		)
-	)
-	configure_mesh(config.mesh(), radio, index, suffix,
-		first_non_nil(
-			mesh_disabled,
-			ibss_disabled,
-			config.mesh.disabled(false)
-		)
-	)
-
 	fixup_wan(radio, index)
 end)
 
diff --git a/package/gluon-core/luasrc/usr/lib/lua/gluon/platform.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/platform.lua
index 5b4f559a6a25c81cb506073d4c5e97c9bb2d88e1..17d881f620df541d4dec1a7635fd37603a131fcf 100644
--- a/package/gluon-core/luasrc/usr/lib/lua/gluon/platform.lua
+++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/platform.lua
@@ -27,3 +27,23 @@ function match(target, subtarget, boards)
 
    return true
 end
+
+function is_outdoor_device()
+   if match('ar71xx', 'generic', {
+      'cpe510-520-v1',
+      'ubnt-nano-m',
+      'ubnt-nano-m-xw',
+      }) then
+      return true
+
+   elseif match('ar71xx', 'generic', {'unifiac-lite'}) and
+	   get_model() == 'Ubiquiti UniFi-AC-MESH' then
+      return true
+
+   elseif match('ar71xx', 'generic', {'unifiac-pro'}) and
+	   get_model() == 'Ubiquiti UniFi-AC-MESH-PRO' then
+      return true
+   end
+
+   return false
+end
diff --git a/package/gluon-web-wifi-config/i18n/de.po b/package/gluon-web-wifi-config/i18n/de.po
index 099e79b59df11f1053e84fb68a315645a48491bb..d3ba4e2bd7ee62dbcdd2e5a293047abc6659dd07 100644
--- a/package/gluon-web-wifi-config/i18n/de.po
+++ b/package/gluon-web-wifi-config/i18n/de.po
@@ -49,3 +49,25 @@ msgstr ""
 "werden. Wenn möglich, ist in den Werten der Sendeleistung der Antennengewinn "
 "enthalten; diese Werte sind allerdings für viele Geräte nicht verfügbar oder "
 "fehlerhaft."
+
+msgid "Outdoor installation"
+msgstr "Outdoor-Installation"
+
+msgid "Node will be installed outdoors"
+msgstr "Knoten wird im Außenbereich betrieben"
+
+msgid ""
+"Configuring the node for outdoor use tunes the 5 GHz radio to a frequency "
+"and transmission power that conforms with the local regulatory requirements. "
+"It also enables dynamic frequency selection (DFS; radar detection). At the "
+"same time, mesh functionality is disabled as it requires neighbouring nodes "
+"to stay on the same channel permanently."
+msgstr ""
+"Ist der Knoten für den Einsatz im Freien konfiguriert, wird ein WLAN-Kanal auf "
+"dem 5-GHz-Band sowie eine Sendeleistung entsprechend den gesetzlichen "
+"Frequenzregulatorien gewählt. Gleichzeitig wird die dynamische Frequenzwahl "
+"(DFS; Radarerkennung) aktiviert und die Mesh-Funktionalität deaktiviert, da "
+"sich Nachbarknoten dauerhaft auf demselben Kanal befinden müssen."
+
+msgid "HT Mode"
+msgstr "HT-Modus"
diff --git a/package/gluon-web-wifi-config/i18n/fr.po b/package/gluon-web-wifi-config/i18n/fr.po
index b019e2fec3a0654089579d08018e67362044192c..faeb01ab7626f6107844f4d3247177aca0fc3365 100644
--- a/package/gluon-web-wifi-config/i18n/fr.po
+++ b/package/gluon-web-wifi-config/i18n/fr.po
@@ -46,3 +46,9 @@ msgstr ""
 "<br /><br />Ici vous pouvez aussi configurer la puissance d'émmission se votre Wi-Fi. "
 "Prenez note que les valeurs fournies pour la puissance de transmission prennent "
 "en compte les gains fournis par l'antenne, et que ces valeurs ne sont pas toujours disponibles ou exactes."
+
+msgid "Outdoor installation"
+msgstr "Installation extérieure"
+
+msgid "HT Mode"
+msgstr "Mode HT"
diff --git a/package/gluon-web-wifi-config/i18n/gluon-web-wifi-config.pot b/package/gluon-web-wifi-config/i18n/gluon-web-wifi-config.pot
index 9b1c8644026800301857ae4cd687e1fa3faf8441..e73666158ae9da41d8a1e1644f06b1443dfd30fe 100644
--- a/package/gluon-web-wifi-config/i18n/gluon-web-wifi-config.pot
+++ b/package/gluon-web-wifi-config/i18n/gluon-web-wifi-config.pot
@@ -33,3 +33,20 @@ msgid ""
 "values include the antenna gain where available, but there are many devices "
 "for which the gain is unavailable or inaccurate."
 msgstr ""
+
+msgid "Outdoor installation"
+msgstr ""
+
+msgid "Node will be installed outdoors"
+msgstr ""
+
+msgid ""
+"Configuring the node for outdoor use tunes the 5 GHz radio to a frequency "
+"and transmission power that conforms with the local regulatory requirements. "
+"It also enables dynamic frequency selection (DFS; radar detection). At the "
+"same time, mesh functionality is disabled as it requires neighbouring nodes "
+"to stay on the same channel permanently."
+msgstr ""
+
+msgid "HT Mode"
+msgstr ""
diff --git a/package/gluon-web-wifi-config/luasrc/lib/gluon/config-mode/model/admin/wifi-config.lua b/package/gluon-web-wifi-config/luasrc/lib/gluon/config-mode/model/admin/wifi-config.lua
index bfb5b9cf088f7b47824d84b413c6fb33093684ae..fc3b231974aa7eab23fe15a7ace157da46c10117 100644
--- a/package/gluon-web-wifi-config/luasrc/lib/gluon/config-mode/model/admin/wifi-config.lua
+++ b/package/gluon-web-wifi-config/luasrc/lib/gluon/config-mode/model/admin/wifi-config.lua
@@ -1,4 +1,5 @@
 local iwinfo = require 'iwinfo'
+local site = require 'gluon.site'
 local uci = require("simple-uci").cursor()
 local util = require 'gluon.util'
 
@@ -8,7 +9,6 @@ local function txpower_list(phy)
 	local off  = tonumber(iwinfo.nl80211.txpower_offset(phy)) or 0
 	local new  = { }
 	local prev = -1
-	local _, val
 	for _, val in ipairs(list) do
 		local dbm = val.dbm + off
 		local mw  = math.floor(10 ^ (dbm / 10))
@@ -24,6 +24,17 @@ local function txpower_list(phy)
 	return new
 end
 
+local function has_5ghz_radio()
+	local result = false
+	uci:foreach('wireless', 'wifi-device', function(config)
+		local radio = config['.name']
+		local hwmode = uci:get('wireless', radio, 'hwmode')
+
+		result = result or (hwmode == '11a' or hwmode == '11na')
+	end)
+
+	return result
+end
 
 local f = Form(translate("WLAN"))
 
@@ -97,7 +108,57 @@ uci:foreach('wireless', 'wifi-device', function(config)
 	end
 end)
 
+
+if has_5ghz_radio() then
+	local r = f:section(Section, translate("Outdoor Installation"), translate(
+		"Configuring the node for outdoor use tunes the 5 GHz radio to a frequency "
+		.. "and transmission power that conforms with the local regulatory requirements. "
+		.. "It also enables dynamic frequency selection (DFS; radar detection). At the "
+		.. "same time, mesh functionality is disabled as it requires neighbouring nodes "
+		.. "to stay on the same channel permanently."
+	))
+
+	local outdoor = r:option(Flag, 'outdoor', translate("Node will be installed outdoors"))
+	outdoor.default = uci:get_bool('gluon', 'wireless', 'outdoor')
+
+	function outdoor:write(data)
+		uci:set('gluon', 'wireless', 'outdoor', data)
+	end
+
+	uci:foreach('wireless', 'wifi-device', function(config)
+		local radio = config['.name']
+		local hwmode = uci:get('wireless', radio, 'hwmode')
+
+		if hwmode ~= '11a' and hwmode ~= '11na' then
+			return
+		end
+
+		local phy = util.find_phy(uci:get_all('wireless', radio))
+
+		local ht = r:option(ListValue, 'outdoor_htmode', translate('HT Mode') .. ' (' .. radio .. ')')
+		ht:depends(outdoor, true)
+		ht.default = uci.get('gluon', 'wireless', 'outdoor_' .. radio .. '_htmode') or 'default'
+
+		ht:value('default', translate("(default)"))
+		for mode, available in pairs(iwinfo.nl80211.htmodelist(phy)) do
+			if available then
+				ht:value(mode, mode)
+			end
+		end
+
+		function ht:write(data)
+			if data == 'default' then
+				data = nil
+			end
+			uci:set('gluon', 'wireless', 'outdoor_' .. radio .. '_htmode', data)
+		end
+	end)
+end
+
+
 function f:write()
+	uci:commit('gluon')
+	os.execute('/lib/gluon/upgrade/200-wireless') 
 	uci:commit('wireless')
 end
 
diff --git a/scripts/check_site.lua b/scripts/check_site.lua
index 9d65bb697e834e09cb2a69b5bea65bb29f71a5e8..507fce83b4c7d80c170abfa31edc1aad83153f85 100644
--- a/scripts/check_site.lua
+++ b/scripts/check_site.lua
@@ -203,6 +203,33 @@ function alternatives(...)
 end
 
 
+local function check_chanlist(channels)
+	local is_valid_channel = check_one_of(channels)
+	return function(chanlist)
+		for group in chanlist:gmatch("%S+") do
+			if group:match("^%d+$") then
+				channel = tonumber(group)
+				if not is_valid_channel(channel) then
+					return false
+				end
+			elseif group:match("^%d+-%d+$") then
+				from, to = group:match("^(%d+)-(%d+)$")
+				from = tonumber(from)
+				to = tonumber(to)
+				if from >= to then
+					return false
+				end
+				if not is_valid_channel(from) or not is_valid_channel(to) then
+					return false
+				end
+			else
+				return false
+			end
+		end
+		return true
+	end
+end
+
 function need(path, check, required, msg)
 	local val = loadvar(path)
 	if required == false and val == nil then
@@ -307,6 +334,12 @@ function need_array_of(path, array, required)
 	return need_array(path, function(e) need_one_of(e, array) end, required)
 end
 
+function need_chanlist(path, channels, required)
+	local valid_chanlist = check_chanlist(channels)
+	return need(path, valid_chanlist, required, 'be a space-separated list of WiFi channels or channel-ranges (separated by a hyphen). ' .. 
+	'Valid channels are: ' .. array_to_string(channels))
+end
+
 function need_domain_name(path)
 	need_string(path)
 	need(path, function(domain_name)