diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py
index 59cea6f09b..2d856d99c5 100644
--- a/esphome/core/__init__.py
+++ b/esphome/core/__init__.py
@@ -689,7 +689,7 @@ class EsphomeCore:
         _LOGGER.debug("Adding: %s", expression)
         return expression
 
-    def add_global(self, expression):
+    def add_global(self, expression, prepend=False):
         from esphome.cpp_generator import Expression, Statement, statement
 
         if isinstance(expression, Expression):
@@ -698,7 +698,10 @@ class EsphomeCore:
             raise ValueError(
                 f"Add '{expression}' must be expression or statement, not {type(expression)}"
             )
-        self.global_statements.append(expression)
+        if prepend:
+            self.global_statements.insert(0, expression)
+        else:
+            self.global_statements.append(expression)
         _LOGGER.debug("Adding global: %s", expression)
         return expression
 
diff --git a/esphome/core/config.py b/esphome/core/config.py
index 06ae1d7747..7152414463 100644
--- a/esphome/core/config.py
+++ b/esphome/core/config.py
@@ -72,6 +72,9 @@ def validate_hostname(config):
 
 
 def valid_include(value):
+    # Look for "<...>" includes
+    if value.startswith("<") and value.endswith(">"):
+        return value
     try:
         return cv.directory(value)
     except cv.Invalid:
@@ -360,7 +363,19 @@ async def to_code(config):
         CORE.add_job(add_arduino_global_workaround)
 
     if config[CONF_INCLUDES]:
-        CORE.add_job(add_includes, config[CONF_INCLUDES])
+        # Get the <...> includes
+        system_includes = []
+        other_includes = []
+        for include in config[CONF_INCLUDES]:
+            if include.startswith("<") and include.endswith(">"):
+                system_includes.append(include)
+            else:
+                other_includes.append(include)
+        # <...> includes should be at the start
+        for include in system_includes:
+            cg.add_global(cg.RawStatement(f"#include {include}"), prepend=True)
+        # Other includes should be at the end
+        CORE.add_job(add_includes, other_includes)
 
     if project_conf := config.get(CONF_PROJECT):
         cg.add_define("ESPHOME_PROJECT_NAME", project_conf[CONF_NAME])
diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py
index 7a82d5cba1..4e283868e1 100644
--- a/esphome/cpp_generator.py
+++ b/esphome/cpp_generator.py
@@ -588,9 +588,9 @@ def add(expression: Union[Expression, Statement]):
     CORE.add(expression)
 
 
-def add_global(expression: Union[SafeExpType, Statement]):
+def add_global(expression: Union[SafeExpType, Statement], prepend: bool = False):
     """Add an expression to the codegen global storage (above setup())."""
-    CORE.add_global(expression)
+    CORE.add_global(expression, prepend)
 
 
 def add_library(name: str, version: Optional[str], repository: Optional[str] = None):