diff --git a/doc/6-configuring-icinga-2.md b/doc/6-configuring-icinga-2.md
index 922de5e5af1..73f48f61e10 100644
--- a/doc/6-configuring-icinga-2.md
+++ b/doc/6-configuring-icinga-2.md
@@ -13,6 +13,7 @@ LocalStateDir       |**Read-only.** Contains the path of the local state directo
 RunDir              |**Read-only.** Contains the path of the run directory. Defaults to LocalStateDir + "/run".
 PkgDataDir          |**Read-only.** Contains the path of the package data directory. Defaults to PrefixDir + "/share/icinga2".
 StatePath           |**Read-write.** Contains the path of the Icinga 2 state file. Defaults to LocalStateDir + "/lib/icinga2/icinga2.state".
+ObjectsPath         |**Read-write.** Contains the path of the Icinga 2 objects file. Defaults to LocalStateDir + "/cache/icinga2/objects.json".
 PidPath             |**Read-write.** Contains the path of the Icinga 2 PID file. Defaults to RunDir + "/icinga2/icinga2.pid".
 Vars                |**Read-write.** Contains a dictionary with global custom attributes. Not set by default.
 NodeName            |**Read-write.** Contains the cluster node name. Set to the local hostname by default.
diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp
index 5cec83f9195..b5ce7963ca9 100644
--- a/icinga-app/icinga.cpp
+++ b/icinga-app/icinga.cpp
@@ -82,7 +82,7 @@ static void IncludeNonLocalZone(const String& zonePath)
 	IncludeZoneDirRecursive(zonePath);
 }
 
-static bool LoadConfigFiles(const String& appType)
+static bool LoadConfigFiles(const String& appType, const String& objectsFile = String())
 {
 	ConfigCompilerContext::GetInstance()->Reset();
 
@@ -113,7 +113,7 @@ static bool LoadConfigFiles(const String& appType)
 	ConfigItem::Ptr item = builder->Compile();
 	item->Register();
 
-	bool result = ConfigItem::ValidateItems();
+	bool result = ConfigItem::ValidateItems(objectsFile);
 
 	int warnings = 0, errors = 0;
 
@@ -382,6 +382,7 @@ int Main(void)
 	}
 
 	Application::DeclareStatePath(Application::GetLocalStateDir() + "/lib/icinga2/icinga2.state");
+	Application::DeclareObjectsPath(Application::GetLocalStateDir() + "/cache/icinga2/objects.json");
 	Application::DeclarePidPath(Application::GetRunDir() + "/icinga2/icinga2.pid");
 
 #ifndef _WIN32
@@ -543,7 +544,7 @@ int Main(void)
 		}
 	}
 
-	if (!LoadConfigFiles(appType))
+	if (!LoadConfigFiles(appType, Application::GetObjectsPath()))
 		return EXIT_FAILURE;
 
 	if (g_AppParams.count("validate")) {
diff --git a/lib/base/application.cpp b/lib/base/application.cpp
index e2a00039db6..49d2d39a079 100644
--- a/lib/base/application.cpp
+++ b/lib/base/application.cpp
@@ -478,6 +478,7 @@ void Application::DisplayInfoMessage(bool skipVersion)
 		  << "  Local state directory: " << GetLocalStateDir() << std::endl
 		  << "  Package data directory: " << GetPkgDataDir() << std::endl
 		  << "  State path: " << GetStatePath() << std::endl
+		  << "  Objects path: " << GetObjectsPath() << std::endl
 		  << "  PID path: " << GetPidPath() << std::endl
 		  << "  Application type: " << GetApplicationType() << std::endl;
 }
@@ -976,6 +977,26 @@ void Application::DeclareStatePath(const String& path)
 	ScriptVariable::Set("StatePath", path, false);
 }
 
+/**
+ * Retrieves the path for the objects file.
+ *
+ * @returns The path.
+ */
+String Application::GetObjectsPath(void)
+{
+	return ScriptVariable::Get("ObjectsPath");
+}
+
+/**
+ * Sets the path for the objects file.
+ *
+ * @param path The new path.
+ */
+void Application::DeclareObjectsPath(const String& path)
+{
+	ScriptVariable::Set("ObjectsPath", path, false);
+}
+
 /**
  * Retrieves the path for the PID file.
  *
@@ -1023,8 +1044,9 @@ void Application::MakeVariablesConstant(void)
 	ScriptVariable::GetByName("LocalStateDir")->SetConstant(true);
 	ScriptVariable::GetByName("RunDir")->SetConstant(true);
 	ScriptVariable::GetByName("PkgDataDir")->SetConstant(true);
-	ScriptVariable::GetByName("StatePath")->SetConstant(false);
-	ScriptVariable::GetByName("PidPath")->SetConstant(false);
+	ScriptVariable::GetByName("StatePath")->SetConstant(true);
+	ScriptVariable::GetByName("ObjectsPath")->SetConstant(true);
+	ScriptVariable::GetByName("PidPath")->SetConstant(true);
 	ScriptVariable::GetByName("ApplicationType")->SetConstant(true);
 }
 
diff --git a/lib/base/application.hpp b/lib/base/application.hpp
index 2213fc6698d..b1d043b6b99 100644
--- a/lib/base/application.hpp
+++ b/lib/base/application.hpp
@@ -104,6 +104,9 @@ class I2_BASE_API Application : public ObjectImpl<Application> {
 	static String GetStatePath(void);
 	static void DeclareStatePath(const String& path);
 
+	static String GetObjectsPath(void);
+	static void DeclareObjectsPath(const String& path);
+
 	static String GetPidPath(void);
 	static void DeclarePidPath(const String& path);
 
diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp
index 80e9d99f944..ece7c9b4c96 100644
--- a/lib/config/configitem.cpp
+++ b/lib/config/configitem.cpp
@@ -30,7 +30,10 @@
 #include "base/debug.hpp"
 #include "base/workqueue.hpp"
 #include "base/exception.hpp"
+#include "base/stdiostream.hpp"
+#include "base/netstring.hpp"
 #include <sstream>
+#include <fstream>
 #include <boost/foreach.hpp>
 
 using namespace icinga;
@@ -264,7 +267,52 @@ void ConfigItem::ValidateItem(void)
 	m_Validated = true;
 }
 
-bool ConfigItem::ValidateItems(void)
+void ConfigItem::WriteObjectsFile(const String& filename)
+{
+	Log(LogInformation, "ConfigItem", "Dumping config items to file '" + filename + "'");
+
+	String tempFilename = filename + ".tmp";
+
+	std::fstream fp;
+	fp.open(tempFilename.CStr(), std::ios_base::out);
+
+	if (!fp)
+		BOOST_THROW_EXCEPTION(std::runtime_error("Could not open '" + tempFilename + "' file"));
+
+	StdioStream::Ptr sfp = make_shared<StdioStream>(&fp, false);
+
+	BOOST_FOREACH(const ItemMap::value_type& kv, m_Items) {
+		ConfigItem::Ptr item = kv.second;
+
+		Dictionary::Ptr persistentItem = make_shared<Dictionary>();
+
+		persistentItem->Set("type", item->GetType());
+		persistentItem->Set("name", item->GetName());
+		persistentItem->Set("abstract", item->IsAbstract());
+		persistentItem->Set("properties", item->GetProperties());
+
+		String json = JsonSerialize(persistentItem);
+
+		NetString::WriteStringToStream(sfp, json);
+	}
+
+	sfp->Close();
+
+	fp.close();
+
+#ifdef _WIN32
+	_unlink(filename.CStr());
+#endif /* _WIN32 */
+
+	if (rename(tempFilename.CStr(), filename.CStr()) < 0) {
+		BOOST_THROW_EXCEPTION(posix_error()
+		    << boost::errinfo_api_function("rename")
+		    << boost::errinfo_errno(errno)
+		    << boost::errinfo_file_name(tempFilename));
+	}
+}
+
+bool ConfigItem::ValidateItems(const String& objectsFile)
 {
 	if (ConfigCompilerContext::GetInstance()->HasErrors())
 		return false;
@@ -323,6 +371,9 @@ bool ConfigItem::ValidateItems(void)
 
 	upq.Join();
 
+	if (!objectsFile.IsEmpty())
+		ConfigItem::WriteObjectsFile(objectsFile);
+
 	ConfigItem::DiscardItems();
 	ConfigType::DiscardTypes();
 
diff --git a/lib/config/configitem.hpp b/lib/config/configitem.hpp
index 7c79cbb03e0..4053161875b 100644
--- a/lib/config/configitem.hpp
+++ b/lib/config/configitem.hpp
@@ -64,10 +64,12 @@ class I2_CONFIG_API ConfigItem : public Object {
 
 	void ValidateItem(void);
 
-	static bool ValidateItems(void);
+	static bool ValidateItems(const String& objectsFile = String());
 	static bool ActivateItems(void);
 	static void DiscardItems(void);
 
+	static void WriteObjectsFile(const String& filename);
+
 private:
 	String m_Type; /**< The object type. */
 	String m_Name; /**< The name. */