diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php
index 0128c66..79db0c2 100644
--- a/core/lib/Drupal/Core/Config/Config.php
+++ b/core/lib/Drupal/Core/Config/Config.php
@@ -208,9 +208,7 @@ public function save($has_trusted_data = FALSE) {
       if ($this->typedConfigManager->hasConfigSchema($this->name)) {
         // Ensure that the schema wrapper has the latest data.
         $this->schemaWrapper = NULL;
-        foreach ($this->data as $key => $value) {
-          $this->data[$key] = $this->castValue($key, $value);
-        }
+        $this->data = $this->castValue(NULL, $this->data);
       }
       else {
         foreach ($this->data as $key => $value) {
diff --git a/core/lib/Drupal/Core/Config/StorableConfigBase.php b/core/lib/Drupal/Core/Config/StorableConfigBase.php
index 40a25ef..a32087a 100644
--- a/core/lib/Drupal/Core/Config/StorableConfigBase.php
+++ b/core/lib/Drupal/Core/Config/StorableConfigBase.php
@@ -3,6 +3,7 @@
 namespace Drupal\Core\Config;
 
 use Drupal\Core\Config\Schema\Ignore;
+use Drupal\Core\Config\Schema\Mapping;
 use Drupal\Core\TypedData\PrimitiveInterface;
 use Drupal\Core\TypedData\Type\FloatInterface;
 use Drupal\Core\TypedData\Type\IntegerInterface;
@@ -164,8 +165,9 @@ protected function validateValue($key, $value) {
   /**
    * Casts the value to correct data type using the configuration schema.
    *
-   * @param string $key
-   *   A string that maps to a key within the configuration data.
+   * @param string|null $key
+   *   A string that maps to a key within the configuration data. If NULL the
+   *   top level mapping will be processed.
    * @param string $value
    *   Value to associate with the key.
    *
@@ -176,7 +178,11 @@ protected function validateValue($key, $value) {
    *   If the value is unsupported in configuration.
    */
   protected function castValue($key, $value) {
-    $element = $this->getSchemaWrapper()->get($key);
+    $element = $this->getSchemaWrapper();
+    if ($key !== NULL) {
+      $element = $element->get($key);
+    }
+
     // Do not cast value if it is unknown or defined to be ignored.
     if ($element && ($element instanceof Undefined || $element instanceof Ignore)) {
       // Do validate the value (may throw UnsupportedDataTypeConfigException)
@@ -208,7 +214,17 @@ protected function castValue($key, $value) {
       }
       // Recurse into any nested keys.
       foreach ($value as $nested_value_key => $nested_value) {
-        $value[$nested_value_key] = $this->castValue($key . '.' . $nested_value_key, $nested_value);
+        $lookup_key = $key ? $key . '.' . $nested_value_key : $nested_value_key;
+        $value[$nested_value_key] = $this->castValue($lookup_key, $nested_value);
+      }
+
+      // Only sort maps when we have more than 1 element to sort.
+      if ($element instanceof Mapping && count($value) > 1) {
+        $mapping = $element->getDataDefinition()['mapping'];
+        // Only sort the keys in $value.
+        $mapping = array_intersect_key($mapping, $value);
+        // Sort the array in $value using the mapping definition.
+        $value = array_replace($mapping, $value);
       }
     }
     return $value;
diff --git a/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml b/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml
index c10e268..e192a30 100644
--- a/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml
+++ b/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml
@@ -297,3 +297,18 @@ test.double_brackets.breed:
   mapping:
     breed:
       type: string
+
+config_schema_test.schema_mapping_sort:
+  type: config_object
+  mapping:
+    bar:
+      type: string
+    foo:
+      type: string
+    map:
+      type: mapping
+      mapping:
+        sub_foo:
+          type: string
+        sub_bar:
+          type: string
diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
index 8a39d64..7cf1fca 100644
--- a/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
@@ -396,6 +396,24 @@ public function testConfigSaveWithSchema() {
   }
 
   /**
+   * Test configuration value data type enforcement using schemas.
+   */
+  public function testConfigSaveMappingSort() {
+    // Top level map sorting.
+    $data = [
+      'foo' => '1',
+      'bar' => '2',
+    ];
+    // Save config which has a schema that enforces types.
+    $this->config('config_schema_test.schema_mapping_sort')
+      ->setData($data)
+      ->save();
+    $this->assertSame(['bar' => '2', 'foo' => '1'], $this->config('config_schema_test.schema_mapping_sort')->get());
+    $this->config('config_schema_test.schema_mapping_sort')->set('map', ['sub_bar' => '2', 'sub_foo' => '1'])->save();
+    $this->assertSame(['sub_foo' => '1', 'sub_bar' => '2'], $this->config('config_schema_test.schema_mapping_sort')->get('map'));
+  }
+
+  /**
    * Tests fallback to a greedy wildcard.
    */
   function testSchemaFallback() {
