diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php
index d014890..40b0116 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php
@@ -82,6 +82,17 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
   protected $languageManager;
 
   /**
+   * Static cache of entities, keyed first by entity ID, then by an extra key.
+   *
+   * The second layer cache key is to maintain separate caches for different
+   * states of config overrides.
+   *
+   * @var array
+   * @see \Drupal\Core\Config\ConfigFactoryInterface::getCacheKey().
+   */
+  protected $entities = array();
+
+  /**
    * Constructs a ConfigEntityStorage object.
    *
    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
@@ -255,6 +266,46 @@ protected function has($id, EntityInterface $entity) {
   }
 
   /**
+   * Gets entities from the static cache.
+   *
+   * @param array $ids
+   *   If not empty, return entities that match these IDs.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface[]
+   *   Array of entities from the entity cache.
+   */
+  protected function getFromStaticCache(array $ids) {
+    $entities = array();
+    // Load any available entities from the internal cache.
+    if ($this->entityType->isStaticallyCacheable() && !empty($this->entities)) {
+      foreach ($ids as $id) {
+        if (!empty($this->entities[$id])) {
+          $key = $this->configFactory->getCacheKey($this->getConfigPrefix() . $id);
+          if (isset($this->entities[$id][$key])) {
+            $entities[$id] = $this->entities[$id][$key];
+          }
+        }
+      }
+    }
+    return $entities;
+  }
+
+  /**
+   * Stores entities in the static entity cache.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface[] $entities
+   *   Entities to store in the cache.
+   */
+  protected function setStaticCache(array $entities) {
+    if ($this->entityType->isStaticallyCacheable()) {
+      foreach ($entities as $id => $entity) {
+        $key = $this->configFactory->getCacheKey($this->getConfigPrefix() . $id);
+        $this->entities[$id][$key] = $entity;
+      }
+    }
+  }
+
+  /**
    * Invokes a hook on behalf of the entity.
    *
    * @param $hook
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
index 6f246f7..3eb08bd 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
@@ -648,9 +648,6 @@ protected function doDelete($entities) {
       $this->invokeFieldMethod('delete', $entity);
       $this->deleteFieldItems($entity);
     }
-
-    // Reset the cache as soon as the changes have been applied.
-    $this->resetCache($ids);
   }
 
   /**
@@ -709,7 +706,6 @@ protected function doSave($id, EntityInterface $entity) {
       if ($this->revisionTable) {
         $entity->setNewRevision(FALSE);
       }
-      $cache_ids = array($entity->id());
     }
     else {
       // Ensure the entity is still seen as new after assigning it an id,
@@ -742,12 +738,9 @@ protected function doSave($id, EntityInterface $entity) {
       if ($this->revisionTable) {
         $entity->setNewRevision(FALSE);
       }
-      // Reset general caches, but keep caches specific to certain entities.
-      $cache_ids = array();
     }
     $this->invokeFieldMethod($is_new ? 'insert' : 'update', $entity);
     $this->saveFieldItems($entity, !$is_new);
-    $this->resetCache($cache_ids);
 
     if (!$is_new && $this->dataTable) {
       $this->invokeTranslationHooks($entity);
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageBase.php b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
index 3dc3bb6..5b831bd 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
@@ -339,14 +339,18 @@ public function delete(array $entities) {
       return;
     }
 
+    // Allow code to run before deleting.
     $entity_class = $this->entityClass;
     $entity_class::preDelete($this, $entities);
     foreach ($entities as $entity) {
       $this->invokeHook('predelete', $entity);
     }
 
+    // Perform the delete and reset the static cache for the deleted entities.
     $this->doDelete($entities);
+    $this->resetCache(array_keys($entities));
 
+    // Allow code to run after deleting.
     $entity_class::postDelete($this, $entities);
     foreach ($entities as $entity) {
       $this->invokeHook('delete', $entity);
@@ -391,8 +395,9 @@ public function save(EntityInterface $entity) {
     $entity->preSave($this);
     $this->invokeHook('presave', $entity);
 
-    // Perform the save.
+    // Perform the save and reset the static cache for the changed entity.
     $return = $this->doSave($id, $entity);
+    $this->resetCache(array($id));
 
     // The entity is no longer new.
     $entity->enforceIsNew(FALSE);
diff --git a/core/modules/config/src/Tests/ConfigEntityStaticCacheTest.php b/core/modules/config/src/Tests/ConfigEntityStaticCacheTest.php
new file mode 100644
index 0000000..d14be09
--- /dev/null
+++ b/core/modules/config/src/Tests/ConfigEntityStaticCacheTest.php
@@ -0,0 +1,96 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\config\Tests\ConfigEntityStaticCacheTest.
+ */
+
+namespace Drupal\config\Tests;
+
+use Drupal\config_entity_static_cache_test\ConfigOverrider;
+use Drupal\simpletest\KernelTestBase;
+
+/**
+ * Tests the entity static cache when used by config entities.
+ *
+ * @group config
+ */
+class ConfigEntityStaticCacheTest extends KernelTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('config_test', 'config_entity_static_cache_test');
+
+  /**
+   * The type ID of the entity under test.
+   *
+   * @var string
+   */
+  protected $entityTypeId;
+
+  /**
+   * The entity ID of the entity under test.
+   *
+   * @var string
+   */
+  protected $entityId;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+    $this->entityTypeId = 'config_test';
+    $this->entityId = 'test_1';
+    entity_create($this->entityTypeId, array('id' => $this->entityId, 'label' => 'Original label'))->save();
+  }
+
+  /**
+   * Tests that the static cache is working.
+   */
+  public function testCacheHit() {
+    $entity_1 = entity_load($this->entityTypeId, $this->entityId);
+    $entity_2 = entity_load($this->entityTypeId, $this->entityId);
+    // config_entity_static_cache_test_config_test_load() sets _loadToken to a
+    // random string. If they match, it means $entity_2 was retrieved from the
+    // static cache rather than going through a separate load sequence.
+    $this->assertIdentical($entity_1->_loadToken, $entity_2->_loadToken);
+  }
+
+  /**
+   * Tests that the static cache is reset on entity save and delete.
+   */
+  public function testReset() {
+    $entity = entity_load($this->entityTypeId, $this->entityId);
+
+    // Ensure loading after a save retrieves the updated entity rather than an
+    // obsolete cached one.
+    $entity->label = 'New label';
+    $entity->save();
+    $entity = entity_load($this->entityTypeId, $this->entityId);
+    $this->assertIdentical($entity->label, 'New label');
+
+    // Ensure loading after a delete retrieves NULL rather than an obsolete
+    // cached one.
+    $entity->delete();
+    $this->assertNull(entity_load($this->entityTypeId, $this->entityId));
+  }
+
+  /**
+   * Tests that the static cache is sensitive to config overrides.
+   */
+  public function testConfigOverride() {
+    // Prime the cache prior to adding a config override.
+    entity_load($this->entityTypeId, $this->entityId);
+
+    // Add the config override, and ensure that what is loaded is correct
+    // despite the prior cache priming.
+    \Drupal::configFactory()->addOverride(new ConfigOverrider());
+    $entity = entity_load($this->entityTypeId, $this->entityId);
+    $this->assertIdentical($entity->label, 'Overridden label');
+  }
+
+}
diff --git a/core/modules/config/tests/config_entity_static_cache_test/config_entity_static_cache_test.info.yml b/core/modules/config/tests/config_entity_static_cache_test/config_entity_static_cache_test.info.yml
new file mode 100644
index 0000000..561d537
--- /dev/null
+++ b/core/modules/config/tests/config_entity_static_cache_test/config_entity_static_cache_test.info.yml
@@ -0,0 +1,5 @@
+name: 'Configuration entity static cache test'
+type: module
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/config/tests/config_entity_static_cache_test/config_entity_static_cache_test.module b/core/modules/config/tests/config_entity_static_cache_test/config_entity_static_cache_test.module
new file mode 100644
index 0000000..92ce503
--- /dev/null
+++ b/core/modules/config/tests/config_entity_static_cache_test/config_entity_static_cache_test.module
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Provides configuration entity static cache test helpers.
+ */
+
+use Drupal\Component\Utility\Random;
+
+/**
+ * Implements hook_ENTITY_TYPE_load().
+ */
+function config_entity_static_cache_test_config_test_load($entities) {
+  static $random;
+  if (!$random) {
+    $random = new Random();
+  }
+  foreach ($entities as $entity) {
+    // Add a random token for every load(), so that during tests, we can tell
+    // if an entity was retrieved from cache (unchanged token) or reloaded.
+    $entity->_loadToken = $random->string(8, TRUE);
+  }
+}
+
+/**
+ * Implements hook_entity_type_alter().
+ */
+function config_entity_static_cache_test_entity_type_alter(array &$entity_types) {
+  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
+  $entity_types['config_test']->set('static_cache', TRUE);
+}
diff --git a/core/modules/config/tests/config_entity_static_cache_test/src/ConfigOverrider.php b/core/modules/config/tests/config_entity_static_cache_test/src/ConfigOverrider.php
new file mode 100644
index 0000000..2867c7d
--- /dev/null
+++ b/core/modules/config/tests/config_entity_static_cache_test/src/ConfigOverrider.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\config_entity_static_cache_test\ConfigOverrider.
+ */
+
+namespace Drupal\config_entity_static_cache_test;
+
+use Drupal\Core\Config\ConfigFactoryOverrideInterface;
+use Drupal\Core\Config\StorageInterface;
+
+/**
+ * Tests module overrides for configuration.
+ */
+class ConfigOverrider implements ConfigFactoryOverrideInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadOverrides($names) {
+    return array(
+      'config_test.dynamic.test_1' => array(
+        'label' => 'Overridden label',
+      )
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheSuffix() {
+    return 'config_entity_static_cache_test';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
+    return NULL;
+  }
+
+}
