diff --git a/core/core.services.yml b/core/core.services.yml
index c158be4..5ef1aa1 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -323,6 +323,9 @@ services:
   entity.query.sql:
     class: Drupal\Core\Entity\Query\Sql\QueryFactory
     arguments: ['@database']
+  entity.query.keyvalue:
+    class: Drupal\Core\Entity\KeyValueStore\Query\QueryFactory
+    arguments: ['@keyvalue']
   router.dumper:
     class: Drupal\Core\Routing\MatcherDumper
     arguments: ['@database']
diff --git a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueEntityStorage.php b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueEntityStorage.php
new file mode 100644
index 0000000..486a090
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueEntityStorage.php
@@ -0,0 +1,174 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage.
+ */
+
+namespace Drupal\Core\Entity\KeyValueStore;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityMalformedException;
+use Drupal\Core\Entity\EntityStorageControllerBase;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a key value backend for entities.
+ */
+class KeyValueEntityStorage extends EntityStorageControllerBase {
+
+  /**
+   * The key value store.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
+   */
+  protected $keyValue;
+
+  /**
+   * Constructs a new KeyValueEntityStorage.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $key_value
+   *   The key value store.
+   */
+  public function __construct(EntityTypeInterface $entity_type, KeyValueStoreInterface $key_value) {
+    parent::__construct($entity_type);
+    $this->keyValue = $key_value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $entity_type,
+      $container->get('keyvalue')->get('entity_storage__' . $entity_type->id())
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function create(array $values = array()) {
+    $entity_class = $this->entityType->getClass();
+    $entity_class::preCreate($this, $values);
+
+    $entity = new $entity_class($values, $this->entityTypeId);
+    $entity->enforceIsNew();
+    $entity->postCreate($this);
+
+    // Modules might need to add or change the data initially held by the new
+    // entity object, for instance to fill-in default values.
+    $this->invokeHook('create', $entity);
+
+    return $entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadMultiple(array $ids = NULL) {
+    if (empty($ids)) {
+      $results = $this->keyValue->getAll();
+    }
+    else {
+      $results = $this->keyValue->getMultiple($ids);
+    }
+    $class = $this->entityType->getClass();
+    $entities = array();
+    foreach ($results as $result) {
+      $entity = new $class($result, $this->entityTypeId);
+      $entities[$entity->id()] = $entity;
+    }
+    return $entities;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function load($id) {
+    $entities = $this->loadMultiple(array($id));
+    return isset($entities[$id]) ? $entities[$id] : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadRevision($revision_id) {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteRevision($revision_id) {
+    return NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete(array $entities) {
+    if (!$entities) {
+      // If no IDs or invalid IDs were passed, do nothing.
+      return;
+    }
+
+    $entity_class = $this->entityType->getClass();
+    $entity_class::preDelete($this, $entities);
+    foreach ($entities as $entity) {
+      $this->invokeHook('predelete', $entity);
+    }
+
+    $entity_ids = array();
+    foreach ($entities as $entity) {
+      $entity_ids[] = $entity->id();
+    }
+    $this->keyValue->deleteMultiple($entity_ids);
+
+    $entity_class::postDelete($this, $entities);
+    foreach ($entities as $entity) {
+      $this->invokeHook('delete', $entity);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(EntityInterface $entity) {
+    $id = $entity->id();
+    if ($id === NULL || $id === '') {
+      throw new EntityMalformedException('The entity does not have an ID.');
+    }
+    $entity->preSave($this);
+    $this->invokeHook('presave', $entity);
+
+    $hook = !$entity->isNew() ? 'update' : 'insert';
+
+    if ($entity instanceof ConfigEntityInterface) {
+      $data = $entity->getExportProperties();
+    }
+    else {
+      // @todo Is there a better way to serialize a non-config entity?
+      $data = $entity;
+    }
+    $this->keyValue->set($entity->id(), $data);
+    $entity->enforceIsNew(FALSE);
+    $entity->postSave($this, FALSE);
+    $this->invokeHook($hook, $entity);
+
+    return $hook == 'update' ? SAVED_UPDATED : SAVED_NEW;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQueryServicename() {
+    return 'entity.query.keyvalue';
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/KeyValueStore/Query/Query.php b/core/lib/Drupal/Core/Entity/KeyValueStore/Query/Query.php
new file mode 100644
index 0000000..d2e37e8
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/KeyValueStore/Query/Query.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\KeyValueStore\Query\Query.
+ */
+
+namespace Drupal\Core\Entity\KeyValueStore\Query;
+
+use Drupal\Core\Config\Entity\Query\Query as QueryParent;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Query\QueryAggregateInterface;
+use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
+
+/**
+ * Defines the entity query for entities stored in a key value backend.
+ */
+class Query extends QueryParent {
+
+  /**
+   * The key value factory.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
+   */
+  protected $keyValueFactory;
+
+  /**
+   * @todo Except the KV factory, this is method is the same as
+   *   \Drupal\Core\Entity\Query\QueryBase. The loadRecords() method is the only
+   *   other part needing to be overridden, otherwise this is identical to
+   *   \Drupal\Core\Config\Entity\Query\Query.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   * @param string $conjunction
+   *   - AND: all of the conditions on the query need to match.
+   *   - OR: at least one of the conditions on the query need to match.
+   * @param array $namespaces
+   *   List of potential namespaces of the classes belonging to this query.
+   * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
+   *   The key value factory.
+   */
+  public function __construct(EntityTypeInterface $entity_type, $conjunction, array $namespaces, KeyValueFactoryInterface $key_value_factory) {
+    $this->entityTypeId = $entity_type->id();
+    $this->entityType = $entity_type;
+    $this->conjunction = $conjunction;
+    $this->namespaces = $namespaces;
+    $this->condition = $this->conditionGroupFactory($conjunction);
+    if ($this instanceof QueryAggregateInterface) {
+      $this->conditionAggregate = $this->conditionAggregateGroupFactory($conjunction);
+    }
+    $this->keyValueFactory = $key_value_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function loadRecords() {
+    return $this->keyValueFactory->get('entity_storage__' . $this->entityTypeId)->getAll();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getClass(array $namespaces, $short_class_name) {
+    // Use the config entity implementation.
+    // @todo Stop doing this.
+    $namespaces = array('\Drupal\Core\Config\Entity\Query');
+    return parent::getClass($namespaces, $short_class_name);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/KeyValueStore/Query/QueryFactory.php b/core/lib/Drupal/Core/Entity/KeyValueStore/Query/QueryFactory.php
new file mode 100644
index 0000000..47de2c5
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/KeyValueStore/Query/QueryFactory.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\KeyValueStore\Query\QueryFactory.
+ */
+
+namespace Drupal\Core\Entity\KeyValueStore\Query;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Query\QueryException;
+use Drupal\Core\Entity\Query\QueryFactoryInterface;
+use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
+
+/**
+ * Provides a factory for creating the key value entity query.
+ */
+class QueryFactory implements QueryFactoryInterface {
+
+  /**
+   * The key value factory.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
+   */
+  protected $keyValueFactory;
+
+  /**
+   * The namespace of this class, the parent class etc.
+   *
+   * @var array
+   */
+  protected $namespaces;
+
+  /**
+   * Constructs a QueryFactory object.
+   *
+   */
+  public function __construct(KeyValueFactoryInterface $key_value_factory) {
+    $this->keyValueFactory = $key_value_factory;
+    $this->namespaces = Query::getNamespaces($this);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get(EntityTypeInterface $entity_type, $conjunction) {
+    return new Query($entity_type, $conjunction, $this->namespaces, $this->keyValueFactory);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAggregate(EntityTypeInterface $entity_type, $conjunction) {
+    throw new QueryException('Aggregation over key-value entity storage is not supported');
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php
new file mode 100644
index 0000000..0e94750
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php
@@ -0,0 +1,284 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Entity\KeyValueStore\KeyValueEntityStorageTest.
+ */
+
+namespace Drupal\Tests\Core\Entity\KeyValueStore {
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Tests\UnitTestCase;
+use Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage
+ *
+ * @group Drupal
+ * @group KeyValueEntityStorage
+ */
+class KeyValueEntityStorageTest extends UnitTestCase {
+
+  /**
+   * @var \PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $entityType;
+  /**
+   * @var \PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $keyValueStore;
+  /**
+   * @var \PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $moduleHandler;
+
+  /**
+   * @var \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage
+   */
+  protected $entityStorage;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'KeyValueEntityStorage',
+      'description' => '',
+      'group' => 'KeyValue Entity Storage',
+    );
+  }
+
+  protected function setUp() {
+    parent::setUp();
+
+    $this->entityType = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $entity = $this->getMockForAbstractClass('Drupal\Core\Entity\Entity', array(), '', FALSE, TRUE, TRUE, array('onSaveOrDelete'));
+    $entity->expects($this->any())
+      ->method('onSaveOrDelete');
+    $this->entityType->expects($this->any())
+      ->method('getClass')
+      ->will($this->returnValue(get_class($entity)));
+    $this->entityType->expects($this->any())
+      ->method('id')
+      ->will($this->returnValue('test_entity_type'));
+    $this->keyValueStore = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
+    $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+    $this->entityStorage = new KeyValueEntityStorage($this->entityType, $this->keyValueStore);
+    $this->entityStorage->setModuleHandler($this->moduleHandler);
+  }
+
+  /**
+   * @covers ::create()
+   */
+  public function testCreate() {
+    $this->moduleHandler->expects($this->at(0))
+      ->method('invokeAll')
+      ->with('test_entity_type_create');
+    $this->moduleHandler->expects($this->at(1))
+      ->method('invokeAll')
+      ->with('entity_create');
+
+    $entity = $this->entityStorage->create(array('id' => 'foo'));
+    $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
+    $this->assertSame('foo', $entity->id());
+    return $entity;
+  }
+
+  /**
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *
+   * @covers ::save()
+   *
+   * @depends testCreate
+   */
+  public function testSaveInsert(EntityInterface $entity) {
+    $this->moduleHandler->expects($this->at(0))
+      ->method('invokeAll')
+      ->with('test_entity_type_presave');
+    $this->moduleHandler->expects($this->at(1))
+      ->method('invokeAll')
+      ->with('entity_presave');
+    $this->moduleHandler->expects($this->at(2))
+      ->method('invokeAll')
+      ->with('test_entity_type_insert');
+    $this->moduleHandler->expects($this->at(3))
+      ->method('invokeAll')
+      ->with('entity_insert');
+    $this->keyValueStore->expects($this->once())
+      ->method('set')
+      ->with('foo', $entity);
+    $return = $this->entityStorage->save($entity);
+    $this->assertSame(SAVED_NEW, $return);
+    return $entity;
+  }
+
+  /**
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *
+   * @depends testSaveInsert
+   */
+  public function testSaveUpdate(EntityInterface $entity) {
+    $this->moduleHandler->expects($this->at(0))
+      ->method('invokeAll')
+      ->with('test_entity_type_presave');
+    $this->moduleHandler->expects($this->at(1))
+      ->method('invokeAll')
+      ->with('entity_presave');
+    $this->moduleHandler->expects($this->at(2))
+      ->method('invokeAll')
+      ->with('test_entity_type_update');
+    $this->moduleHandler->expects($this->at(3))
+      ->method('invokeAll')
+      ->with('entity_update');
+    $this->keyValueStore->expects($this->once())
+      ->method('set')
+      ->with('foo', $entity);
+    $return = $this->entityStorage->save($entity);
+    $this->assertSame(SAVED_UPDATED, $return);
+    return $entity;
+  }
+
+  /**
+   * @covers ::save()
+   * @expectedException \Drupal\Core\Entity\EntityMalformedException
+   * @expectedExceptionMessage The entity does not have an ID.
+   */
+  public function testSaveInvalid() {
+    $entity = $this->entityStorage->create(array());
+    $this->entityStorage->save($entity);
+  }
+
+  /**
+   * @covers ::load()
+   */
+  public function testLoad() {
+    $this->keyValueStore->expects($this->once())
+      ->method('getMultiple')
+      ->with(array('foo'))
+      ->will($this->returnValue(array(array('id' => 'foo'))));
+    $entity = $this->entityStorage->load('foo');
+    $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
+    $this->assertSame('foo', $entity->id());
+  }
+
+  /**
+   * @covers ::loadMultiple()
+   */
+  public function testLoadMultipleAll() {
+    $expected[] = $this->getMockEntity();
+    $expected[] = $this->getMockEntity();
+    $this->keyValueStore->expects($this->once())
+      ->method('getAll')
+      ->will($this->returnValue(array(array('id' => 'foo'), array('id' => 'bar'))));
+    $entities = $this->entityStorage->loadMultiple();
+    foreach ($entities as $id => $entity) {
+      $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
+      $this->assertSame($id, $entity->id());
+    }
+  }
+
+  /**
+   * @covers ::loadMultiple()
+   */
+  public function testLoadMultipleIds() {
+    $expected[] = $this->getMockEntity();
+    $this->keyValueStore->expects($this->once())
+      ->method('getMultiple')
+      ->with(array('foo'))
+      ->will($this->returnValue(array(array('id' => 'foo'))));
+    $entities = $this->entityStorage->loadMultiple(array('foo'));
+    foreach ($entities as $id => $entity) {
+      $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
+      $this->assertSame($id, $entity->id());
+    }
+  }
+
+  /**
+   * @covers ::loadRevision()
+   */
+  public function testLoadRevision() {
+    $this->assertSame(FALSE, $this->entityStorage->loadRevision(1));
+  }
+
+  /**
+   * @covers ::deleteRevision()
+   */
+  public function testDeleteRevision() {
+    $this->assertSame(NULL, $this->entityStorage->deleteRevision(1));
+  }
+
+  /**
+   * @covers ::delete()
+   */
+  public function testDelete() {
+    $entities = array();
+    foreach (array('foo', 'bar') as $id) {
+      $entity = $this->getMockEntity(array('id'));
+      $entity->expects($this->once())
+        ->method('id')
+        ->will($this->returnValue($id));
+      $entities[] = $entity;
+    }
+    $this->moduleHandler->expects($this->at(0))
+      ->method('invokeAll')
+      ->with('test_entity_type_predelete');
+    $this->moduleHandler->expects($this->at(1))
+      ->method('invokeAll')
+      ->with('entity_predelete');
+    $this->moduleHandler->expects($this->at(2))
+      ->method('invokeAll')
+      ->with('test_entity_type_predelete');
+    $this->moduleHandler->expects($this->at(3))
+      ->method('invokeAll')
+      ->with('entity_predelete');
+    $this->moduleHandler->expects($this->at(4))
+      ->method('invokeAll')
+      ->with('test_entity_type_delete');
+    $this->moduleHandler->expects($this->at(5))
+      ->method('invokeAll')
+      ->with('entity_delete');
+    $this->moduleHandler->expects($this->at(6))
+      ->method('invokeAll')
+      ->with('test_entity_type_delete');
+    $this->moduleHandler->expects($this->at(7))
+      ->method('invokeAll')
+      ->with('entity_delete');
+
+    $this->keyValueStore->expects($this->once())
+      ->method('deleteMultiple')
+      ->with(array('foo', 'bar'));
+    $this->entityStorage->delete($entities);
+  }
+
+  /**
+   * @covers ::delete()
+   */
+  public function testDeleteNothing() {
+    $this->moduleHandler->expects($this->never())
+      ->method($this->anything());
+    $this->keyValueStore->expects($this->never())
+      ->method('deleteMultiple');
+
+    $this->entityStorage->delete(array());
+  }
+
+  protected function getMockEntity($methods = array()) {
+    $methods[] = 'onSaveOrDelete';
+    $entity = $this->getMockForAbstractClass('Drupal\Core\Entity\Entity', array(), '', FALSE, TRUE, TRUE, $methods);
+    $entity->expects($this->any())
+      ->method('onSaveOrDelete');
+    return $entity;
+  }
+
+}
+
+}
+namespace {
+  if (!defined('SAVED_NEW')) {
+    define('SAVED_NEW', 1);
+  }
+  if (!defined('SAVED_UPDATED')) {
+    define('SAVED_UPDATED', 2);
+  }
+}
