diff --git a/config/vufind/searchspecs.yaml b/config/vufind/searchspecs.yaml
index e945c79ad982bd74ef8ab3ba1fb11e78f923a8d4..62e12e5d243f06e5b00e44b60d075df05e5c7036 100644
--- a/config/vufind/searchspecs.yaml
+++ b/config/vufind/searchspecs.yaml
@@ -148,6 +148,19 @@
 # [uppercase] - Convert string to uppercase
 #
 # See the CallNumber search below for an example of custom munging in action.
+#
+#-----------------------------------------------------------------------------------
+#
+# Note that you may create a "@parent_yaml" entry at the very top of the file to
+# inherit sections from another file. For example:
+#
+# @parent_yaml: "/path/to/my/file.yaml"
+#
+# Only sections not found in this file will be loaded in from the parent file.
+# In some complex scenarios, this can be a useful way of sharing settings
+# between multiple configured VuFind instances. You can create a chain of parent
+# files if necessary.
+#
 #-----------------------------------------------------------------------------------
 
 # These searches use Dismax when possible:
diff --git a/module/VuFind/src/VuFind/Config/SearchSpecsReader.php b/module/VuFind/src/VuFind/Config/SearchSpecsReader.php
index 8bc6b752e9fca8f11b684477c7e5ba47582a8834..d772c346e30ba51cc73be9f42cb18387234205c6 100644
--- a/module/VuFind/src/VuFind/Config/SearchSpecsReader.php
+++ b/module/VuFind/src/VuFind/Config/SearchSpecsReader.php
@@ -134,6 +134,13 @@ class SearchSpecsReader
         $results = (!empty($file) && file_exists($file))
             ? Yaml::parse(file_get_contents($file)) : [];
 
+        // Override default parent with explicitly-defined parent, if present:
+        if (isset($results['@parent_yaml'])) {
+            $defaultParent = $results['@parent_yaml'];
+            // Swallow the directive after processing it:
+            unset($results['@parent_yaml']);
+        }
+
         // Now load in missing sections from parent, if applicable:
         if (null !== $defaultParent) {
             foreach ($this->parseYaml($defaultParent) as $section => $contents) {
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php
index ce7e7fdf408c907e239bc3c520b2b6db762f9826..52d8c0f294722b12b7c986f5f04618488bf2e542 100644
--- a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php
@@ -26,7 +26,7 @@
  * @link     https://vufind.org/wiki/development:testing:unit_tests Wiki
  */
 namespace VuFindTest\Config;
-use VuFind\Config\SearchSpecsReader;
+use VuFind\Config\Locator, VuFind\Config\SearchSpecsReader;
 
 /**
  * Config SearchSpecsReader Test Class
@@ -40,6 +40,49 @@ use VuFind\Config\SearchSpecsReader;
  */
 class SearchSpecsReaderTest extends \VuFindTest\Unit\TestCase
 {
+    /**
+     * Flag -- did writing config files fail?
+     *
+     * @var bool
+     */
+    protected static $writeFailed = false;
+
+    /**
+     * Array of files to clean up after test.
+     *
+     * @var array
+     */
+    protected static $filesToDelete = [];
+
+    /**
+     * Standard setup method.
+     *
+     * @return void
+     */
+    public static function setUpBeforeClass()
+    {
+        // Create test files:
+        $parentPath = Locator::getLocalConfigPath('top.yaml', null, true);
+        $parent = "top: foo";
+        $childPath = Locator::getLocalConfigPath('middle.yaml', null, true);
+        $child = "@parent_yaml: $parentPath\nmiddle: bar";
+        $grandchildPath = Locator::getLocalConfigPath('bottom.yaml', null, true);
+        $grandchild = "@parent_yaml: $childPath\nbottom: baz";
+
+        // Fail if we are unable to write files:
+        if (null === $parentPath || null === $childPath || null === $grandchildPath
+            || !file_put_contents($parentPath, $parent)
+            || !file_put_contents($childPath, $child)
+            || !file_put_contents($grandchildPath, $grandchild)
+        ) {
+            self::$writeFailed = true;
+            return;
+        }
+
+        // Mark for cleanup:
+        self::$filesToDelete = [$parentPath, $childPath, $grandchildPath];
+    }
+
     /**
      * Test loading of a YAML file.
      *
@@ -114,4 +157,38 @@ class SearchSpecsReaderTest extends \VuFindTest\Unit\TestCase
             $this->callMethod($reader, 'getFromPaths', [$core, $local])
         );
     }
+
+    /**
+     * Test @parent_yaml directive.
+     *
+     * @return void
+     */
+    public function testParentYaml()
+    {
+        if (self::$writeFailed) {
+            $this->markTestSkipped('Could not write test configurations.');
+        }
+        $reader = new SearchSpecsReader();
+        $core = Locator::getLocalConfigPath('middle.yaml', null, true);
+        $local = Locator::getLocalConfigPath('bottom.yaml', null, true);
+        $this->assertEquals(
+            [
+                'top' => 'foo',
+                'middle' => 'bar',
+                'bottom' => 'baz',
+            ],
+            $this->callMethod($reader, 'getFromPaths', [$core, $local])
+        );
+    }
+
+    /**
+     * Standard teardown method.
+     *
+     * @return void
+     */
+    public static function tearDownAfterClass()
+    {
+        // Clean up test files:
+        array_map('unlink', self::$filesToDelete);
+    }
 }