import argparse
 import base64
+import contextlib
 import copy
 import dataclasses
 import enum
 import uuid
 from collections.abc import Collection, Iterable, Iterator, Sequence
 from pathlib import Path
-from typing import Any, Callable, Collection, Optional, Union, cast
+from typing import Any, Callable, Optional, Type, Union, cast
 
 from mkosi.architecture import Architecture
 from mkosi.distributions import Distribution, detect_distribution
 def config_make_list_parser(delimiter: str,
                             *,
                             parse: Callable[[str], Any] = str,
-                            unescape: bool = False) -> ConfigParseCallback:
+                            unescape: bool = False,
+                            reset: bool = True) -> ConfigParseCallback:
     def config_parse_list(value: Optional[str], old: Optional[list[Any]]) -> Optional[list[Any]]:
         new = old.copy() if old else []
 
             values = value.replace(delimiter, "\n").split("\n")
 
         # Empty strings reset the list.
-        if len(values) == 1 and values[0] == "":
+        if reset and len(values) == 1 and values[0] == "":
             return None
 
         return new + [parse(v) for v in values if v]
         logging.warning(f"{option_string} is no longer supported")
 
 
-def config_make_action(settings: Sequence[MkosiConfigSetting]) -> type[argparse.Action]:
-    lookup = {s.dest: s for s in settings}
-
-    class MkosiAction(argparse.Action):
-        def __call__(
-            self,
-            parser: argparse.ArgumentParser,
-            namespace: argparse.Namespace,
-            values: Union[str, Sequence[Any], None],
-            option_string: Optional[str] = None
-        ) -> None:
-            assert option_string is not None
-
-            if values is None and self.nargs == "?":
-                values = self.const or "yes"
-
-            try:
-                s = lookup[self.dest]
-            except KeyError:
-                die(f"Unknown setting {option_string}")
-
-            if values is None or isinstance(values, str):
-                setattr(namespace, s.dest, s.parse(values, getattr(namespace, self.dest, None)))
-            else:
-                for v in values:
-                    assert isinstance(v, str)
-                    setattr(namespace, s.dest, s.parse(v, getattr(namespace, self.dest, None)))
-
-    return MkosiAction
-
-
 class PagerHelpAction(argparse._HelpAction):
     def __call__(
         self,
     access the value from state.
     """
 
+    include: tuple[str, ...]
     presets: tuple[str]
     dependencies: tuple[str]
 
 
 
 SETTINGS = (
+    MkosiConfigSetting(
+        dest="include",
+        section="Config",
+        parse=config_make_list_parser(delimiter=",", reset=False, parse=make_path_parser()),
+        help="Include configuration from the specified file or directory",
+    ),
     MkosiConfigSetting(
         dest="presets",
         long="--preset",
 )
 
 
-def create_argument_parser() -> argparse.ArgumentParser:
-    action = config_make_action(SETTINGS)
-
+def create_argument_parser(action: Type[argparse.Action]) -> argparse.ArgumentParser:
     parser = argparse.ArgumentParser(
         prog="mkosi",
         description="Build Bespoke OS Images",
     settings_lookup_by_dest = {s.dest: s for s in SETTINGS}
     match_lookup = {m.name: m for m in MATCHES}
 
+    @contextlib.contextmanager
+    def parse_new_includes(
+        namespace: argparse.Namespace,
+        defaults: argparse.Namespace,
+    ) -> Iterator[None]:
+        l = len(getattr(namespace, "include", []))
+
+        try:
+            yield
+        finally:
+            # Parse any includes that were added after yielding.
+            for p in getattr(namespace, "include", [])[l:]:
+                parse_config(p, namespace, defaults)
+
+    class MkosiAction(argparse.Action):
+        def __call__(
+            self,
+            parser: argparse.ArgumentParser,
+            namespace: argparse.Namespace,
+            values: Union[str, Sequence[Any], None],
+            option_string: Optional[str] = None
+        ) -> None:
+            assert option_string is not None
+
+            if values is None and self.nargs == "?":
+                values = self.const or "yes"
+
+            try:
+                s = settings_lookup_by_dest[self.dest]
+            except KeyError:
+                die(f"Unknown setting {option_string}")
+
+            with parse_new_includes(namespace, defaults):
+                if values is None or isinstance(values, str):
+                    setattr(namespace, s.dest, s.parse(values, getattr(namespace, self.dest, None)))
+                else:
+                    for v in values:
+                        assert isinstance(v, str)
+                        setattr(namespace, s.dest, s.parse(v, getattr(namespace, self.dest, None)))
+
     def finalize_default(
         setting: MkosiConfigSetting,
         namespace: argparse.Namespace,
         else:
             default = setting.default
 
-        setattr(namespace, setting.dest, default)
+        with parse_new_includes(namespace, defaults):
+            setattr(namespace, setting.dest, default)
+
         return default
 
     def match_config(path: Path, namespace: argparse.Namespace, defaults: argparse.Namespace) -> bool:
                     canonical = s.name if k == name else f"@{s.name}"
                     logging.warning(f"Setting {k} is deprecated, please use {canonical} instead.")
 
-                setattr(ns, s.dest, s.parse(v, getattr(ns, s.dest, None)))
+                with parse_new_includes(namespace, defaults):
+                    setattr(ns, s.dest, s.parse(v, getattr(ns, s.dest, None)))
 
         if extras:
             for s in SETTINGS:
         argv += ["--", "build"]
 
     namespace = argparse.Namespace()
-    argparser = create_argument_parser()
+    argparser = create_argument_parser(MkosiAction)
     argparser.parse_args(argv, namespace)
 
     args = load_args(namespace)
                           Verb: {bold(args.verb)}
                        Cmdline: {bold(" ".join(args.cmdline))}
 
+    {bold("CONFIG")}:
+                       Include: {line_join_list(config.include)}
+
     {bold("PRESET")}:
                        Presets: {line_join_list(config.presets)}
                   Dependencies: {line_join_list(config.dependencies)}
 
 import itertools
 import logging
 import operator
+import os
 from pathlib import Path
 from typing import Optional
 
 import pytest
 
 from mkosi.architecture import Architecture
-from mkosi.config import Compression, OutputFormat, Verb, parse_config, parse_ini
+from mkosi.config import (
+    Compression,
+    ConfigFeature,
+    OutputFormat,
+    Verb,
+    parse_config,
+    parse_ini,
+)
 from mkosi.distributions import Distribution
 from mkosi.util import chdir
 
     # ImageVersion= is not set explicitly anymore, so now the version from mkosi.version should be used.
     assert config.image_version == "1.2.3"
 
+    (tmp_path / "abc").mkdir()
+    (tmp_path / "abc/mkosi.conf").write_text(
+        """\
+        [Content]
+        Bootable=yes
+        """
+    )
+    (tmp_path / "abc/mkosi.conf.d").mkdir()
+    (tmp_path / "abc/mkosi.conf.d/abc.conf").write_text(
+        """\
+        [Output]
+        SplitArtifacts=yes
+        """
+    )
+
+    with chdir(tmp_path):
+        _, [config] = parse_config()
+        assert config.bootable == ConfigFeature.auto
+        assert config.split_artifacts == False
+
+        # Passing the directory should include both the main config file and the dropin.
+        _, [config] = parse_config(["--include", os.fspath(tmp_path / "abc")])
+        assert config.bootable == ConfigFeature.enabled
+        assert config.split_artifacts == True
+
+        # Passing the main config file should not include the dropin.
+        _, [config] = parse_config(["--include", os.fspath(tmp_path / "abc/mkosi.conf")])
+        assert config.bootable == ConfigFeature.enabled
+        assert config.split_artifacts == False
+
 
 def test_parse_load_verb(tmp_path: Path) -> None:
     (tmp_path / "mkosi.conf").write_text("[Distribution]\nDistribution=fedora")
                 f"""\
                 [Distribution]
                 Distribution=fedora
-                
+
                 [{section}]
                 ImageId=testimage
                 """