diff --git a/doc/4-monitoring-remote-systems.md b/doc/4-monitoring-remote-systems.md
index 376000053..a3bb3e407 100644
--- a/doc/4-monitoring-remote-systems.md
+++ b/doc/4-monitoring-remote-systems.md
@@ -344,6 +344,35 @@ process.
> determines the required include directory. This can be overridden using the
> [global constant](#global-constants) `ZonesDir`.
+#### Global configuration zone
+
+If your zone configuration setup shares the same templates, groups, commands, timeperiods, etc.
+you would have to duplicate quite a lot of configuration objects making the merged configuration
+on your configuration master unique.
+
+That is not necessary by defining a global zone shipping all those templates. By settting
+`global = true` you ensure that this zone configuration template will be synchronized to all
+involved nodes (only if they accept configuration though).
+
+ /etc/icinga2/zones.d
+ global-templates/
+ templates.conf
+ groups.conf
+ master
+ health.conf
+ checker
+ health.conf
+ demo.conf
+
+In this example, the global zone is called `global-templates` and must be defined in
+your zone configuration visible to all nodes.
+
+ object Zone "global-templates" {
+ global = true
+ }
+
+If you don't require any global configuration, skip this setting.
+
#### Zone Configuration Permissions
Each [ApiListener](#objecttype-apilistener) object must have the `accept_config` attribute
diff --git a/etc/icinga2/zones.conf b/etc/icinga2/zones.conf
index e7a1d99ce..6ad1537cc 100644
--- a/etc/icinga2/zones.conf
+++ b/etc/icinga2/zones.conf
@@ -12,6 +12,16 @@ object Zone ZoneName {
endpoints = [ NodeName ]
}
+/*
+ * Defines a global zone containing templates,
+ * etc. synced to all nodes, if they accept
+ * configuration.
+ */
+
+object Zone "global-templates" {
+ global = true
+}
+
/*
* Read the documentation on how to configure
* a cluster setup with multiple zones.
diff --git a/lib/remote/apilistener-sync.cpp b/lib/remote/apilistener-sync.cpp
index 6c13711fe..81ce77be9 100644
--- a/lib/remote/apilistener-sync.cpp
+++ b/lib/remote/apilistener-sync.cpp
@@ -126,6 +126,12 @@ void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const
void ApiListener::SyncZoneDirs(void) const
{
BOOST_FOREACH(const Zone::Ptr& zone, DynamicType::GetObjects()) {
+ /* always sync global zone dirs */
+ if (zone->GetGlobal()) {
+ SyncZoneDir(zone);
+ continue;
+ }
+
if (!IsConfigMaster(zone))
continue;
diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp
index 5cd0c0411..cac4f93b9 100644
--- a/lib/remote/apilistener.hpp
+++ b/lib/remote/apilistener.hpp
@@ -105,8 +105,10 @@ private:
static Dictionary::Ptr LoadConfigDir(const String& dir);
static bool UpdateConfigDir(const Dictionary::Ptr& oldConfig, const Dictionary::Ptr& newConfig, const String& configDir);
+
void SyncZoneDirs(void) const;
void SyncZoneDir(const Zone::Ptr& zone) const;
+
bool IsConfigMaster(const Zone::Ptr& zone) const;
static void ConfigGlobHandler(const Dictionary::Ptr& config, const String& path, const String& file);
void SendConfigUpdate(const ApiClient::Ptr& aclient);
diff --git a/lib/remote/remote-type.conf b/lib/remote/remote-type.conf
index a4abff727..c59a8e918 100644
--- a/lib/remote/remote-type.conf
+++ b/lib/remote/remote-type.conf
@@ -47,5 +47,7 @@
%attribute %array "endpoints" {
%attribute %name(Endpoint) "*"
- }
+ },
+
+ %attribute %number "global"
}
diff --git a/lib/remote/zone.ti b/lib/remote/zone.ti
index d2f3433f0..743f8cb99 100644
--- a/lib/remote/zone.ti
+++ b/lib/remote/zone.ti
@@ -7,6 +7,7 @@ class Zone : DynamicObject
{
[config] String parent (ParentRaw);
[config] Array::Ptr endpoints (EndpointsRaw);
+ [config] bool global;
};
}