diff --git a/doc/user.xml b/doc/user.xml
index a2d7a3d04..89d00a19c 100644
--- a/doc/user.xml
+++ b/doc/user.xml
@@ -418,6 +418,14 @@ systemctl start mpd.socket</programlisting>
       </para>
 
       <programlisting>include "other.conf"</programlisting>
+
+      <para>
+        You can use <varname>include_optional</varname> instead if you
+        want the included file to be optional; the directive will be
+        ignored if the file does not exist:
+      </para>
+
+      <programlisting>include_optional "may_not_exist.conf"</programlisting>
     </section>
 
     <section id="config_music_directory">
diff --git a/src/config/File.cxx b/src/config/File.cxx
index 89c57fc9a..5552f71b7 100644
--- a/src/config/File.cxx
+++ b/src/config/File.cxx
@@ -28,6 +28,7 @@
 #include "util/StringAPI.hxx"
 #include "util/Domain.hxx"
 #include "util/RuntimeError.hxx"
+#include "fs/FileSystem.hxx"
 #include "fs/Path.hxx"
 #include "fs/io/FileReader.hxx"
 #include "fs/io/BufferedReader.hxx"
@@ -179,13 +180,20 @@ ReadConfigFile(ConfigData &config_data, BufferedReader &reader, Path directory)
 			// TODO: detect recursion
 			// TODO: Config{Block,Param} have only line number but no file name
 			// TODO: support wildcards (include "conf.d/*.conf")
-			// TODO: add "include_optional"
 			const auto path = AllocatedPath::Apply(directory,
 							       AllocatedPath::FromUTF8Throw(ExpectValueAndEnd(tokenizer)));
 			ReadConfigFile(config_data, path);
 			continue;
 		}
 
+		if (StringIsEqual(name, "include_optional")) {
+			const auto path = AllocatedPath::Apply(directory,
+							       AllocatedPath::FromUTF8Throw(ExpectValueAndEnd(tokenizer)));
+			if (PathExists(path))
+				ReadConfigFile(config_data, path);
+			continue;
+		}
+
 		/* get the definition of that option, and check the
 		   "repeatable" flag */