diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index e26cb36..cb7092b 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -504,9 +504,12 @@ function config_get_config_directory() {
     // @see Drupal\simpletest\WebTestBase::setUp()
     $path = conf_path() . '/files/simpletest/' . substr($test_prefix, 10) . '/config';
   }
-  else {
+  elseif (!empty($config_directory_name)) {
     $path = conf_path() . '/files/' . $config_directory_name;
   }
+  else {
+    throw new \Exception('Missing $config_directory_name.');
+  }
   return $path;
 }
 
@@ -2424,20 +2427,24 @@ function drupal_get_bootstrap_phase() {
  *
  * @see Drupal\Core\DrupalKernel
  *
- * @param $reset
- *   A new container instance to reset the Drupal container to.
+ * @param Symfony\Component\DependencyInjection\Container $new_container
+ *   A new container instance to replace the current.
+ * @param bool $reset
+ *   (optional) Internal use only. Whether to enforce a reset of the statically
+ *   cached container. Pass NULL for $new_container to recreate a new Container
+ *   from scratch in a subsequent call to this function. Used by tests.
  *
  * @return Symfony\Component\DependencyInjection\Container
  *   The instance of the Container used to set up and maintain object
  *   instances.
  */
-function drupal_container(Container $reset = NULL) {
+function drupal_container(Container $new_container = NULL, $reset = FALSE) {
   // We do not use drupal_static() here because we do not have a mechanism by
   // which to reinitialize the stored objects, so a drupal_static_reset() call
   // would leave Drupal in a nonfunctional state.
   static $container = NULL;
-  if (isset($reset)) {
-    $container = $reset;
+  if (isset($new_container) || $reset) {
+    $container = $new_container;
   }
   elseif (!isset($container)) {
     // Return a ContainerBuilder instance with the bare essentials needed for any
@@ -2452,15 +2459,27 @@ function drupal_container(Container $reset = NULL) {
     //   bootstrap configuration *file* to allow to set/override this very
     //   lowest of low level configuration.
     $container->setParameter('config.storage.options', array(
-      'connection' => 'default',
-      'target' => 'default',
+      'Drupal\Core\Config\FileStorage' => array(
+        'directory' => config_get_config_directory() . '/active',
+      ),
+      'Drupal\Core\Config\CacheStorage' => array(
+        'backend' => 'Drupal\Core\Cache\DatabaseBackend',
+        'bin' => 'config',
+      ),
     ));
-    $container->register('config.storage', 'Drupal\Core\Config\DatabaseStorage')
+    $container->register('config.storage', 'Drupal\Core\Config\CachedFileStorage')
       ->addArgument('%config.storage.options%');
 
     // Register configuration object factory.
     $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
       ->addArgument(new Reference('config.storage'));
+
+    // Register configuration state.
+    $container->setParameter('config.state.options', array(
+      'directory' => config_get_config_directory() . '/import',
+    ));
+    $container->register('config.state', 'Drupal\Core\Config\FileStorage')
+      ->addArgument('%config.state.options%');
   }
   return $container;
 }
diff --git a/core/includes/config.inc b/core/includes/config.inc
index f2fcd39..14f41f9 100644
--- a/core/includes/config.inc
+++ b/core/includes/config.inc
@@ -17,9 +17,6 @@
  *   The extension type; e.g., 'module' or 'theme'.
  * @param string $name
  *   The name of the module or theme to install default configuration for.
- *
- * @todo Make this acknowledge other storage engines rather than having
- *   SQL be hardcoded.
  */
 function config_install_default_config($type, $name) {
   $config_dir = drupal_get_path($type, $name) . '/config';
@@ -43,7 +40,9 @@ function config_install_default_config($type, $name) {
 }
 
 /**
- * @todo Modules need a way to access the active store, whatever it is.
+ * Gets configuration object names starting with a given prefix.
+ *
+ * @see Drupal\Core\Config\StorageInterface::listAll()
  */
 function config_get_storage_names_with_prefix($prefix = '') {
   return drupal_container()->get('config.storage')->listAll($prefix);
@@ -129,15 +128,15 @@ function config_sync_changes(array $config_changes, StorageInterface $source_sto
 }
 
 /**
- * Imports configuration from FileStorage to the active store.
+ * Imports configuration into the active store.
  *
  * @return bool|null
  *   TRUE if configuration was imported successfully, FALSE in case of a
  *   synchronization error, or NULL if there are no changes to synchronize.
  */
 function config_import() {
-  // Retrieve a list of differences between FileStorage and the active store.
-  $source_storage = new FileStorage();
+  // Retrieve a list of differences between last known state and active store.
+  $source_storage = drupal_container()->get('config.state');
   $target_storage = drupal_container()->get('config.storage');
 
   $config_changes = config_sync_get_changes($source_storage, $target_storage);
@@ -217,12 +216,14 @@ function config_import_invoke_owner(array $config_changes, StorageInterface $sou
 }
 
 /**
- * Exports configuration from the active store to FileStorage.
+ * Updates the last known state with the active store configuration.
+ *
+ * @todo config_export() is a misnomer now. Rename to config_state_update().
  */
 function config_export() {
-  // Retrieve a list of differences between the active store and FileStorage.
+  // Retrieve a list of differences between active store and last known state.
   $source_storage = drupal_container()->get('config.storage');
-  $target_storage = new FileStorage();
+  $target_storage = drupal_container()->get('config.state');
 
   $config_changes = config_sync_get_changes($source_storage, $target_storage);
   if (empty($config_changes)) {
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 369e868..0f010e5 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -4,6 +4,8 @@
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\Install\TaskException;
 
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 
@@ -250,6 +252,12 @@ function install_begin_request(&$install_state) {
     exit;
   }
 
+  // Initialize conf_path().
+  // This primes the site path to be used during installation. By not requiring
+  // settings.php, a bare site folder can be prepared in the /sites directory,
+  // which will be used for installing Drupal.
+  conf_path(FALSE);
+
   drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
 
   // A request object from the HTTPFoundation to tell us about the request.
@@ -269,6 +277,38 @@ function install_begin_request(&$install_state) {
   include_once DRUPAL_ROOT . '/core/includes/module.inc';
   include_once DRUPAL_ROOT . '/core/includes/session.inc';
 
+  // Determine whether the configuration system is ready to operate.
+  $install_state['config_verified'] = install_verify_config_directory();
+
+  // If it is not, replace the configuration storage with the InstallStorage
+  // implementation, for the following reasons:
+  // - The first call into drupal_container() will try to set up the regular
+  //   runtime configuration storage, using the CachedFileStorage. The storage
+  //   controller options require to pass in the config directory name, but that
+  //   directory does not exist yet.
+  // - The installer outputs maintenance theme pages and performs many other
+  //   operations, which try to load configuration. Since there is no active
+  //   configuration yet, and because the configuration system does not have a
+  //   notion of default values at runtime, data is missing in many places. The
+  //   lack of data does not trigger errors, but results in a broken user
+  //   interface (e.g., missing titles, etc).
+  // - The actual configuration data to read during installation is essentially
+  //   the default configuration provided by the installation profile and
+  //   modules (most notably System module). The InstallStorage therefore reads
+  //   from the default configuration directories.
+  // This override is reverted as soon as the config directory has been set up
+  // successfully.
+  // @see drupal_install_config_directory()
+  // @todo Move this into a proper Drupal\Core\DependencyInjection\InstallContainerBuilder.
+  if (!$install_state['config_verified']) {
+    $container = new ContainerBuilder();
+    $container->register('config.storage', 'Drupal\Core\Config\InstallStorage');
+    $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
+      ->addArgument(new Reference('config.storage'));
+
+    drupal_container($container);
+  }
+
   // Set up $language, so t() caller functions will still work.
   drupal_language_initialize();
 
@@ -313,7 +353,6 @@ function install_begin_request(&$install_state) {
 
   // Check existing settings.php.
   $install_state['database_verified'] = install_verify_database_settings();
-  $install_state['config_verified'] = install_ensure_config_directory();
   $install_state['settings_verified'] = $install_state['config_verified'] && $install_state['database_verified'];
 
   if ($install_state['database_verified']) {
@@ -1049,6 +1088,11 @@ function install_settings_form_submit($form, &$form_state) {
   // Add the config directory to settings.php.
   drupal_install_config_directory();
 
+  // We have a valid configuration directory in settings.php.
+  // Reset the service container, so the config.storage service will use the
+  // regular configuration storage for the remainder of the installation.
+  drupal_container(NULL, TRUE);
+
   // Indicate that the settings file has been verified, and check the database
   // for the last completed task, now that we have a valid connection. This
   // last step is important since we want to trigger an error if the new
diff --git a/core/includes/install.inc b/core/includes/install.inc
index 4c9c60c..0a4e3f5 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -259,7 +259,7 @@ function drupal_install_config_directory() {
 
   // Add a randomized config directory name to settings.php, unless it was
   // manually defined in the existing already.
-  if (!$config_directory_name) {
+  if (empty($config_directory_name)) {
     $settings['config_directory_name'] = array(
       'value' => 'config_' . drupal_hash_base64(drupal_random_bytes(55)),
       'required' => TRUE,
@@ -284,6 +284,27 @@ function drupal_install_config_directory() {
 }
 
 /**
+ * Checks whether a config directory name is defined, and if so, whether it
+ * exists and is writable.
+ *
+ * This partically duplicates install_ensure_config_directory(), but is required
+ * since the installer would create the config directory too early in the
+ * installation process otherwise (e.g., when only visiting install.php when
+ * there is a settings.php already, but not actually executing the installation).
+ */
+function install_verify_config_directory() {
+  global $config_directory_name;
+  if (empty($config_directory_name)) {
+    return FALSE;
+  }
+  $config_directory = config_get_config_directory();
+  if (is_dir($config_directory) && is_writable($config_directory)) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
  * Ensures that the config directory exists and is writable, or can be made so.
  */
 function install_ensure_config_directory() {
@@ -296,7 +317,12 @@ function install_ensure_config_directory() {
   // directories that the installer creates.
   else {
     $config_directory = config_get_config_directory();
-    return file_prepare_directory($config_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+    $success = file_prepare_directory($config_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+    foreach (array('active', 'import') as $subdir) {
+      $subdir = $config_directory . '/' . $subdir;
+      $success = $success && file_prepare_directory($subdir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+    }
+    return $success;
   }
 }
 
diff --git a/core/includes/module.inc b/core/includes/module.inc
index ab45576..6c4f24d 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -6,7 +6,7 @@
  */
 
 use Drupal\Component\Graph\Graph;
-use Drupal\Core\Config\DatabaseStorage;
+use Drupal\Core\Config\NullStorage;
 
 /**
  * Load all the modules that have been enabled in the system table.
@@ -622,19 +622,26 @@ function module_uninstall($module_list = array(), $uninstall_dependents = TRUE)
     $module_list = array_keys($module_list);
   }
 
-  $storage = new DatabaseStorage();
+  $source_storage = new NullStorage();
+  $target_storage = drupal_container()->get('config.storage');
   foreach ($module_list as $module) {
+    // Remove all configuration belonging to the module.
+    $config_changes = $target_storage->listAll($module . '.');
+    if (!empty($config_changes)) {
+      $config_changes = array(
+        'delete' => $config_changes,
+        'change' => array(),
+        'create' => array(),
+      );
+      $remaining_changes = config_import_invoke_owner($config_changes, $source_storage, $target_storage);
+      config_sync_changes($remaining_changes, $source_storage, $target_storage);
+    }
+
     // Uninstall the module.
     module_load_install($module);
     module_invoke($module, 'uninstall');
     drupal_uninstall_schema($module);
 
-    // Remove all configuration belonging to the module.
-    $config_names = $storage->listAll($module . '.');
-    foreach ($config_names as $config_name) {
-      config($config_name)->delete();
-    }
-
     watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO);
     drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED);
   }
diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc
index 52b6c40..12de2a7 100644
--- a/core/includes/theme.maintenance.inc
+++ b/core/includes/theme.maintenance.inc
@@ -106,6 +106,7 @@ function _theme_load_offline_registry($theme, $base_theme = NULL, $theme_engine
  * @ingroup themeable
  */
 function theme_task_list($variables) {
+  $t = get_t();
   $items = $variables['items'];
   $active = $variables['active'];
 
@@ -116,7 +117,7 @@ function theme_task_list($variables) {
   foreach ($items as $k => $item) {
     if ($active == $k) {
       $class = 'active';
-      $status = '(' . t('active') . ')';
+      $status = '(' . $t('active') . ')';
       $done = FALSE;
     }
     else {
diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
index e90e2ad..255af8f 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Cache;
 
+use Drupal\Core\Database\Database;
 use Exception;
 
 /**
@@ -53,14 +54,14 @@ function get($cid) {
    */
   function getMultiple(&$cids) {
     try {
-      // When serving cached pages, the overhead of using db_select() was found
+      // When serving cached pages, the overhead of using ::select() was found
       // to add around 30% overhead to the request. Since $this->bin is a
-      // variable, this means the call to db_query() here uses a concatenated
+      // variable, this means the call to ::query() here uses a concatenated
       // string. This is highly discouraged under any other circumstances, and
       // is used here only due to the performance overhead we would incur
       // otherwise. When serving an uncached page, the overhead of using
-      // db_select() is a much smaller proportion of the request.
-      $result = db_query('SELECT cid, data, created, expire, serialized, tags, checksum FROM {' . db_escape_table($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids));
+      // ::select() is a much smaller proportion of the request.
+      $result = Database::getConnection()->query('SELECT cid, data, created, expire, serialized, tags, checksum FROM {' . Database::getConnection()->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids));
       $cache = array();
       foreach ($result as $item) {
         $item = $this->prepareItem($item);
@@ -135,7 +136,7 @@ function set($cid, $data, $expire = CACHE_PERMANENT, array $tags = array()) {
     }
 
     try {
-      db_merge($this->bin)
+      Database::getConnection()->merge($this->bin)
         ->key(array('cid' => $cid))
         ->fields($fields)
         ->execute();
@@ -149,7 +150,7 @@ function set($cid, $data, $expire = CACHE_PERMANENT, array $tags = array()) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::delete().
    */
   function delete($cid) {
-    db_delete($this->bin)
+    Database::getConnection()->delete($this->bin)
       ->condition('cid', $cid)
       ->execute();
   }
@@ -160,7 +161,7 @@ function delete($cid) {
   function deleteMultiple(array $cids) {
     // Delete in chunks when a large array is passed.
     do {
-      db_delete($this->bin)
+      Database::getConnection()->delete($this->bin)
         ->condition('cid', array_splice($cids, 0, 1000), 'IN')
         ->execute();
     }
@@ -171,8 +172,8 @@ function deleteMultiple(array $cids) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::deletePrefix().
    */
   function deletePrefix($prefix) {
-    db_delete($this->bin)
-      ->condition('cid', db_like($prefix) . '%', 'LIKE')
+    Database::getConnection()->delete($this->bin)
+      ->condition('cid', Database::getConnection()->escapeLike($prefix) . '%', 'LIKE')
       ->execute();
   }
 
@@ -180,14 +181,14 @@ function deletePrefix($prefix) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::flush().
    */
   function flush() {
-    db_truncate($this->bin)->execute();
+    Database::getConnection()->truncate($this->bin)->execute();
   }
 
   /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::expire().
    */
   function expire() {
-    db_delete($this->bin)
+    Database::getConnection()->delete($this->bin)
       ->condition('expire', CACHE_PERMANENT, '<>')
       ->condition('expire', REQUEST_TIME, '<')
       ->execute();
@@ -250,7 +251,7 @@ protected function flattenTags(array $tags) {
   public function invalidateTags(array $tags) {
     foreach ($this->flattenTags($tags) as $tag) {
       unset(self::$tagCache[$tag]);
-      db_merge('cache_tags')
+      Database::getConnection()->merge('cache_tags')
         ->key(array('tag' => $tag))
         ->fields(array('invalidations' => 1))
         ->expression('invalidations', 'invalidations + 1')
@@ -280,9 +281,14 @@ protected function checksumTags($tags) {
       }
    }
     if ($query_tags) {
-      if ($db_tags = db_query('SELECT tag, invalidations FROM {cache_tags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllKeyed()) {
-        self::$tagCache = array_merge(self::$tagCache, $db_tags);
-        $checksum += array_sum($db_tags);
+      try {
+        if ($db_tags = Database::getConnection()->query('SELECT tag, invalidations FROM {cache_tags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllKeyed()) {
+          self::$tagCache = array_merge(self::$tagCache, $db_tags);
+          $checksum += array_sum($db_tags);
+        }
+      }
+      catch (Exception $e) {
+        // The database may not be available, so we'll ignore cache_set requests.
       }
     }
     return $checksum;
@@ -293,7 +299,7 @@ protected function checksumTags($tags) {
    */
   function isEmpty() {
     $this->garbageCollection();
-    $query = db_select($this->bin);
+    $query = Database::getConnection()->select($this->bin);
     $query->addExpression('1');
     $result = $query->range(0, 1)
       ->execute()
diff --git a/core/lib/Drupal/Core/Config/CacheStorage.php b/core/lib/Drupal/Core/Config/CacheStorage.php
new file mode 100644
index 0000000..1f69c87
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/CacheStorage.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Config\CacheStorage.
+ */
+
+namespace Drupal\Core\Config;
+
+/**
+ * Defines the Cache storage controller.
+ *
+ * This storage controller essentially just bridges the configuration system
+ * storage to a backend of the cache system. All StorageInterface method calls
+ * are forwarded to the corresponding CacheBackendInterface methods.
+ *
+ * @see Drupal\Core\Cache\CacheBackendInterface
+ *
+ * While this causes a level of indirection and the functionality could
+ * technically be embedded into a configuration storage controller directly,
+ * a separate bridge is architecturally cleaner and also allows for alternative
+ * setups.
+ *
+ * By default, the configuration system uses this controller in the
+ * CachedFileStorage implementation.
+ *
+ * @see Drupal\Core\Config\CachedFileStorage
+ */
+class CacheStorage implements StorageInterface {
+
+  /**
+   * Cache backend options for this storage controller.
+   *
+   * - backend: The cache backend to use.
+   * - bin: The cache bin to use.
+   *
+   * @var array
+   */
+  protected $options;
+
+  /**
+   * The instantiated Cache backend.
+   *
+   * @var Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $storage;
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::__construct().
+   */
+  public function __construct(array $options = array()) {
+    $options += array(
+      'backend' => 'Drupal\Core\Cache\DatabaseBackend',
+      'bin' => 'config',
+    );
+    $this->options = $options;
+  }
+
+  /**
+   * Returns the instantiated Cache backend to use.
+   */
+  protected function getBackend() {
+    if (!isset($this->storage)) {
+      $this->storage = new $this->options['backend']($this->options['bin']);
+    }
+    return $this->storage;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::exists().
+   */
+  public function exists($name) {
+    return (bool) $this->getBackend()->get($name);
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::read().
+   */
+  public function read($name) {
+    if ($cache = $this->getBackend()->get($name)) {
+      // The cache backend supports primitive data types, but only an array
+      // represents valid config object data.
+      if (is_array($cache->data)) {
+        return $cache->data;
+      }
+    }
+    return FALSE;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::write().
+   */
+  public function write($name, array $data) {
+    $this->getBackend()->set($name, $data, CACHE_PERMANENT, array('config' => array($name)));
+    return TRUE;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::delete().
+   */
+  public function delete($name) {
+    $this->getBackend()->delete($name);
+    return TRUE;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::encode().
+   */
+  public static function encode($data) {
+    return serialize($data);
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::decode().
+   *
+   * @throws ErrorException
+   *   unserialize() triggers E_NOTICE if the string cannot be unserialized.
+   */
+  public static function decode($raw) {
+    $data = @unserialize($raw);
+    return is_array($data) ? $data : FALSE;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::listAll().
+   *
+   * Not supported by CacheBackendInterface.
+   */
+  public function listAll($prefix = '') {
+    return array();
+  }
+}
diff --git a/core/lib/Drupal/Core/Config/CachedFileStorage.php b/core/lib/Drupal/Core/Config/CachedFileStorage.php
new file mode 100644
index 0000000..74eb99e
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/CachedFileStorage.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Config\CachedFileStorage.
+ */
+
+namespace Drupal\Core\Config;
+
+use Drupal\Core\Config\FileStorage;
+
+/**
+ * Storage controller that uses FileStorage as canonical storage and
+ * CacheStorage as a cache.
+ */
+class CachedFileStorage implements StorageInterface {
+
+  /**
+   * The configuration options for the sub-storage controllers.
+   *
+   * An associative array whose keys are the class names of the storage
+   * controllers to use, and whose values are the respective configuration
+   * options for each storage controller.
+   *
+   * Two array elements are expected. A key and definition for
+   * Drupal\Core\Config\FileStorage is required. The other element should point
+   * to another storage controller that is used as cache.
+   *
+   * @var array
+   */
+  protected $options;
+
+  /**
+   * The instantiated sub-storage controllers.
+   *
+   * @var array
+   */
+  protected $storages = array();
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::__construct().
+   */
+  public function __construct(array $options = array()) {
+    $this->options = $options;
+
+    $this->storages['file'] = new FileStorage($options['Drupal\Core\Config\FileStorage']);
+
+    unset($options['Drupal\Core\Config\FileStorage']);
+    list($cache_class, $cache_options) = each($options);
+    $this->storages['cache'] = new $cache_class($cache_options);
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::exists().
+   */
+  public function exists($name) {
+    // A single filestat is faster than a complex cache lookup and possibly
+    // subsequent filestat.
+    return $this->storages['file']->exists($name);
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::read().
+   */
+  public function read($name) {
+    // Check the cache.
+    $data = $this->storages['cache']->read($name);
+    // If the cache returns no result, check the file storage.
+    if ($data === FALSE) {
+      $data = $this->storages['file']->read($name);
+      // @todo Should the config object be cached if it does not exist?
+      if ($data !== FALSE) {
+        $this->storages['cache']->write($name, $data);
+      }
+    }
+    return $data;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::write().
+   */
+  public function write($name, array $data) {
+    $success = $this->storages['file']->write($name, $data);
+    $this->storages['cache']->delete($name);
+    return $success;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::delete().
+   */
+  public function delete($name) {
+    $success = TRUE;
+    foreach ($this->storages as $storage) {
+      if (!$storage->delete($name)) {
+        $success = FALSE;
+      }
+    }
+    return $success;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::encode().
+   *
+   * @todo Remove encode() from StorageInterface.
+   */
+  public static function encode($data) {
+    return $this->storages['file']->encode($data);
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::decode().
+   *
+   * @todo Remove decode() from StorageInterface.
+   */
+  public static function decode($raw) {
+    return $this->storages['file']->decode($raw);
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::listAll().
+   */
+  public function listAll($prefix = '') {
+    return $this->storages['file']->listAll($prefix);
+  }
+}
diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php
index 2f4d14a..c5423ca 100644
--- a/core/lib/Drupal/Core/Config/Config.php
+++ b/core/lib/Drupal/Core/Config/Config.php
@@ -270,6 +270,7 @@ public function sortByKey(array &$data) {
    * Deletes the configuration object.
    */
   public function delete() {
+    // @todo Consider to remove the pruning of data for Config::delete().
     $this->data = array();
     $this->storage->delete($this->name);
     $this->isNew = TRUE;
diff --git a/core/lib/Drupal/Core/Config/DatabaseStorage.php b/core/lib/Drupal/Core/Config/DatabaseStorage.php
index 92aea45..8ab64f6 100644
--- a/core/lib/Drupal/Core/Config/DatabaseStorage.php
+++ b/core/lib/Drupal/Core/Config/DatabaseStorage.php
@@ -44,6 +44,15 @@ protected function getConnection() {
   }
 
   /**
+   * Implements Drupal\Core\Config\StorageInterface::exists().
+   */
+  public function exists($name) {
+    return (bool) $this->getConnection()->queryRange('SELECT 1 FROM {config} WHERE name = :name', 0, 1, array(
+      ':name' => $name,
+    ), $this->options)->fetchField();
+  }
+
+  /**
    * Implements Drupal\Core\Config\StorageInterface::read().
    *
    * @throws PDOException
diff --git a/core/lib/Drupal/Core/Config/FileStorage.php b/core/lib/Drupal/Core/Config/FileStorage.php
index 033555d..727adef 100644
--- a/core/lib/Drupal/Core/Config/FileStorage.php
+++ b/core/lib/Drupal/Core/Config/FileStorage.php
@@ -54,10 +54,7 @@ public static function getFileExtension() {
   }
 
   /**
-   * Returns whether the configuration file exists.
-   *
-   * @return bool
-   *   TRUE if the configuration file exists, FALSE otherwise.
+   * Implements Drupal\Core\Config\StorageInterface::exists().
    */
   public function exists($name) {
     return file_exists($this->getFilePath($name));
diff --git a/core/lib/Drupal/Core/Config/InstallStorage.php b/core/lib/Drupal/Core/Config/InstallStorage.php
new file mode 100644
index 0000000..67cf357
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/InstallStorage.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\InstallStorage.
+ */
+
+namespace Drupal\Core\Config;
+
+/**
+ * Storage controller used by the Drupal installer.
+ */
+class InstallStorage extends FileStorage {
+
+  /**
+   * Configuration options for this storage controller.
+   *
+   * @var array
+   */
+  protected $options;
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::__construct().
+   */
+  public function __construct(array $options = array()) {
+    // Intentionally not calling FileStorage's constructor, since that calls
+    // into config_get_config_directory(), which is not functional yet.
+    $this->options = $options;
+  }
+
+  /**
+   * Overrides Drupal\Core\Config\FileStorage::getFilePath().
+   *
+   * Returns the path to the configuration file.
+   *
+   * This essentially attempts to determine the owner and path to the default
+   * configuration file of a requested config object name located in the
+   * installation profile, a module, or a theme.
+   *
+   * @return string
+   *   The path to the configuration file.
+   *
+   * @todo Improve this when figuring out how we want to handle configuration in
+   *   installation profiles.
+   */
+  public function getFilePath($name) {
+    // Extract the owner.
+    $owner = strtok($name, '.');
+    // Determine the path to the owner.
+    $path = FALSE;
+    foreach (array('profile', 'module', 'theme') as $type) {
+      if ($path = drupal_get_path($type, $owner)) {
+        break;
+      }
+    }
+    return $path . '/config/' . $name . '.' . self::getFileExtension();
+  }
+
+  /**
+   * Overrides Drupal\Core\Config\FileStorage::write().
+   *
+   * @throws Drupal\Core\Config\StorageException
+   */
+  public function write($name, array $data) {
+    throw new StorageException('Write operations are not allowed.');
+  }
+
+  /**
+   * Overrides Drupal\Core\Config\FileStorage::delete().
+   */
+  public function delete($name) {
+    throw new StorageException('Write operations are not allowed.');
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::listAll().
+   */
+  public function listAll($prefix = '') {
+    throw new StorageException('List operation is not supported.');
+  }
+}
diff --git a/core/lib/Drupal/Core/Config/NullStorage.php b/core/lib/Drupal/Core/Config/NullStorage.php
index fede4f0..2e1c06c 100644
--- a/core/lib/Drupal/Core/Config/NullStorage.php
+++ b/core/lib/Drupal/Core/Config/NullStorage.php
@@ -29,6 +29,13 @@ public function __construct(array $options = array()) {
   }
 
   /**
+   * Implements Drupal\Core\Config\StorageInterface::exists().
+   */
+  public function exists($name) {
+    return FALSE;
+  }
+
+  /**
    * Implements Drupal\Core\Config\StorageInterface::read().
    */
   public function read($name) {
diff --git a/core/lib/Drupal/Core/Config/StorageInterface.php b/core/lib/Drupal/Core/Config/StorageInterface.php
index 806ee87..4fb9869 100644
--- a/core/lib/Drupal/Core/Config/StorageInterface.php
+++ b/core/lib/Drupal/Core/Config/StorageInterface.php
@@ -25,6 +25,17 @@
   public function __construct(array $options = array());
 
   /**
+   * Returns whether a configuration object exists.
+   *
+   * @param string $name
+   *   The name of a configuration object to test.
+   *
+   * @return bool
+   *   TRUE if the configuration object exists, FALSE otherwise.
+   */
+  public function exists($name);
+
+  /**
    * Reads configuration data from the storage.
    *
    * @param string $name
diff --git a/core/modules/config/config.admin.inc b/core/modules/config/config.admin.inc
new file mode 100644
index 0000000..eefa104
--- /dev/null
+++ b/core/modules/config/config.admin.inc
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @file
+ * Admin page callbacks for the config module.
+ */
+
+use Drupal\Core\Config\StorageInterface;
+
+/**
+ * Helper function to construct the storage changes in a configuration synchronization form.
+ *
+ * @param array $form
+ *   The form structure to add to. Passed by reference.
+ * @param array $form_state
+ *   The current state of the form. Passed by reference.
+ * @param Drupal\Core\Config\StorageInterface $source_storage
+ *   The source storage to retrieve differences from.
+ * @param Drupal\Core\Config\StorageInterface $target_storage
+ *   The target storage to compare differences to.
+ */
+function config_admin_sync_form(array &$form, array &$form_state, StorageInterface $source_storage, StorageInterface $target_storage) {
+  $config_changes = config_sync_get_changes($source_storage, $target_storage);
+  if (empty($config_changes)) {
+    $form['no_changes'] = array(
+      '#markup' => t('There are no configuration changes.'),
+    );
+    return $form;
+  }
+
+  foreach ($config_changes as $config_change_type => $config_files) {
+    if (empty($config_files)) {
+      continue;
+    }
+    $form[$config_change_type] = array(
+      '#type' => 'fieldset',
+      '#title' => $config_change_type . ' (' . count($config_files) . ')',
+      '#collapsible' => TRUE,
+    );
+    $form[$config_change_type]['config_files'] = array(
+      '#theme' => 'table',
+      '#header' => array('Name'),
+    );
+    foreach ($config_files as $config_file) {
+      $form[$config_change_type]['config_files']['#rows'][] = array($config_file);
+    }
+  }
+}
+
+/**
+ * Form constructor for configuration import form.
+ *
+ * @see config_admin_import_form_submit()
+ * @see config_import()
+ */
+function config_admin_import_form($form, &$form_state) {
+  // Retrieve a list of differences between last known state and active store.
+  $source_storage = drupal_container()->get('config.state');
+  $target_storage = drupal_container()->get('config.storage');
+
+  // Prevent users from deleting all configuration.
+  // If the source storage is empty, that signals the unique condition of not
+  // having exported anything at all, and thus no valid storage to compare the
+  // active storage against.
+  // @todo StorageInterface::listAll() can easily yield hundreds or even
+  //   thousands of entries; consider to add a dedicated isEmpty() method for
+  //   storage controllers.
+  $all = $source_storage->listAll();
+  if (empty($all)) {
+    form_set_error('', t('There is no base configuration. <a href="@export-url">Export</a> it first.', array(
+      '@export-url' => url('admin/config/development/sync/export'),
+    )));
+    return $form;
+  }
+
+  config_admin_sync_form($form, $form_state, $source_storage, $target_storage);
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Import'),
+  );
+  return $form;
+}
+
+/**
+ * Form submission handler for config_admin_import_form().
+ */
+function config_admin_import_form_submit($form, &$form_state) {
+  if (config_import()) {
+    drupal_set_message(t('The configuration was imported successfully.'));
+  }
+  else {
+    // Another request may be synchronizing configuration already. Wait for it
+    // to complete before returning the error, so already synchronized changes
+    // do not appear again.
+    lock_wait(__FUNCTION__);
+    drupal_set_message(t('The import failed due to an error. Any errors have been logged.'), 'error');
+  }
+}
+
+/**
+ * Form constructor for configuration export form.
+ *
+ * @see config_admin_export_form_submit()
+ * @see config_export()
+ *
+ * @todo "export" is a misnomer with config.state.
+ */
+function config_admin_export_form($form, &$form_state) {
+  // Retrieve a list of differences between active store and last known state.
+  $source_storage = drupal_container()->get('config.storage');
+  $target_storage = drupal_container()->get('config.state');
+
+  config_admin_sync_form($form, $form_state, $source_storage, $target_storage);
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Export'),
+  );
+  return $form;
+}
+
+/**
+ * Form submission handler for config_admin_export_form().
+ */
+function config_admin_export_form_submit($form, &$form_state) {
+  config_export();
+  drupal_set_message(t('The configuration was exported successfully.'));
+}
+
diff --git a/core/modules/config/config.info b/core/modules/config/config.info
index 380f17e..efab7a1 100644
--- a/core/modules/config/config.info
+++ b/core/modules/config/config.info
@@ -3,3 +3,4 @@ description = Allows administrators to manage configuration changes.
 package = Core
 version = VERSION
 core = 8.x
+configure = admin/config/development/sync
diff --git a/core/modules/config/config.module b/core/modules/config/config.module
index b3d9bbc..fd38fd2 100644
--- a/core/modules/config/config.module
+++ b/core/modules/config/config.module
@@ -1 +1,46 @@
 <?php
+
+/**
+ * @file
+ * Allows site administrators to modify configuration.
+ */
+
+/**
+ * Implements hook_permission().
+ */
+function config_permission() {
+  $permissions['synchronize configuration'] = array(
+    'title' => t('Synchronize configuration'),
+    'restrict access' => TRUE,
+  );
+  return $permissions;
+}
+
+/**
+ * Implements hook_menu().
+ */
+function config_menu() {
+  $items['admin/config/development/sync'] = array(
+    'title' => 'Synchronize configuration',
+    'description' => 'Synchronize configuration changes.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('config_admin_import_form'),
+    'access arguments' => array('synchronize configuration'),
+    'file' => 'config.admin.inc',
+  );
+  $items['admin/config/development/sync/import'] = array(
+    'title' => 'Import',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['admin/config/development/sync/export'] = array(
+    'title' => 'Export',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('config_admin_export_form'),
+    'access arguments' => array('synchronize configuration'),
+    'file' => 'config.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+  return $items;
+}
+
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php
index 37aa854..6bb34eb 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\config\Tests;
 
-use Drupal\Core\Config\DatabaseStorage;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -26,7 +25,7 @@ public static function getInfo() {
    * Tests CRUD operations.
    */
   function testCRUD() {
-    $storage = new DatabaseStorage();
+    $storage = $this->container->get('config.storage');
     $name = 'config_test.crud';
 
     $config = config($name);
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php
index abbd2ae..7fadf31 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\config\Tests;
 
-use Drupal\Core\Config\DatabaseStorage;
 use Drupal\Core\Config\FileStorage;
 use Drupal\simpletest\WebTestBase;
 
@@ -23,15 +22,11 @@ public static function getInfo() {
     );
   }
 
-  function setUp() {
-    parent::setUp();
-  }
-
   /**
    * Tests setting, writing, and reading of a configuration setting.
    */
   function testReadWriteConfig() {
-    $database_storage = new DatabaseStorage();
+    $storage = $this->container->get('config.storage');
 
     $name = 'foo.bar';
     $key = 'foo';
@@ -67,7 +62,7 @@ function testReadWriteConfig() {
     $this->assertEqual($config->get(), array(), t('New config object is empty.'));
 
     // Verify nothing was saved.
-    $db_data = $database_storage->read($name);
+    $db_data = $storage->read($name);
     $this->assertIdentical($db_data, FALSE);
 
     // Add a top level value
@@ -94,7 +89,7 @@ function testReadWriteConfig() {
     $config->save();
 
     // Verify the database entry exists.
-    $db_data = $database_storage->read($name);
+    $db_data = $storage->read($name);
     $this->assertTrue($db_data);
 
     // Read top level value
@@ -152,27 +147,27 @@ function testReadWriteConfig() {
     $config->set($key, $value)->save();
 
     // Verify the database entry exists from a chained save.
-    $db_data = $database_storage->read($chained_name);
+    $db_data = $storage->read($chained_name);
     $this->assertEqual($db_data, $config->get());
 
     // Get file listing for all files starting with 'foo'. Should return
     // two elements.
-    $files = $database_storage->listAll('foo');
+    $files = $storage->listAll('foo');
     $this->assertEqual(count($files), 2, 'Two files listed with the prefix \'foo\'.');
 
     // Get file listing for all files starting with 'biff'. Should return
     // one element.
-    $files = $database_storage->listAll('biff');
+    $files = $storage->listAll('biff');
     $this->assertEqual(count($files), 1, 'One file listed with the prefix \'biff\'.');
 
     // Get file listing for all files starting with 'foo.bar'. Should return
     // one element.
-    $files = $database_storage->listAll('foo.bar');
+    $files = $storage->listAll('foo.bar');
     $this->assertEqual(count($files), 1, 'One file listed with the prefix \'foo.bar\'.');
 
     // Get file listing for all files starting with 'bar'. Should return
     // an empty array.
-    $files = $database_storage->listAll('bar');
+    $files = $storage->listAll('bar');
     $this->assertEqual($files, array(), 'No files listed with the prefix \'bar\'.');
 
     // Delete the configuration.
@@ -180,7 +175,7 @@ function testReadWriteConfig() {
     $config->delete();
 
     // Verify the database entry no longer exists.
-    $db_data = $database_storage->read($name);
+    $db_data = $storage->read($name);
     $this->assertIdentical($db_data, FALSE);
   }
 
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php
index ec92fdf..f834b72 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php
@@ -7,8 +7,6 @@
 
 namespace Drupal\config\Tests;
 
-use Drupal\Core\Config\DatabaseStorage;
-use Drupal\Core\Config\FileStorage;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -37,6 +35,8 @@ public static function getInfo() {
   function testDeleted() {
     $name = 'config_test.system';
     $dynamic_name = 'config_test.dynamic.default';
+    $storage = $this->container->get('config.storage');
+    $state = $this->container->get('config.state');
 
     // Verify the default configuration values exist.
     $config = config($name);
@@ -48,17 +48,15 @@ function testDeleted() {
     config_export();
 
     // Delete the configuration objects.
-    $file_storage = new FileStorage();
-    $file_storage->delete($name);
-    $file_storage->delete($dynamic_name);
+    $state->delete($name);
+    $state->delete($dynamic_name);
 
     // Import.
     config_import();
 
     // Verify the values have disappeared.
-    $database_storage = new DatabaseStorage();
-    $this->assertIdentical($database_storage->read($name), FALSE);
-    $this->assertIdentical($database_storage->read($dynamic_name), FALSE);
+    $this->assertIdentical($storage->read($name), FALSE);
+    $this->assertIdentical($storage->read($dynamic_name), FALSE);
 
     $config = config($name);
     $this->assertIdentical($config->get('foo'), NULL);
@@ -72,34 +70,40 @@ function testDeleted() {
   function testNew() {
     $name = 'config_test.new';
     $dynamic_name = 'config_test.dynamic.new';
-
-    // Verify the configuration to create does not exist yet.
-    $file_storage = new FileStorage();
-    $this->assertIdentical($file_storage->exists($name), FALSE, $name . ' not found.');
-    $this->assertIdentical($file_storage->exists($dynamic_name), FALSE, $dynamic_name . ' not found.');
+    $storage = $this->container->get('config.storage');
+    $state = $this->container->get('config.state');
 
     // Export.
     config_export();
 
+    // Verify the configuration to create does not exist yet.
+    $this->assertIdentical($storage->exists($name), FALSE, $name . ' not found.');
+    $this->assertIdentical($storage->exists($dynamic_name), FALSE, $dynamic_name . ' not found.');
+
+    $this->assertIdentical($state->exists($name), FALSE, $name . ' not found.');
+    $this->assertIdentical($state->exists($dynamic_name), FALSE, $dynamic_name . ' not found.');
+
     // Create new configuration objects.
-    $file_storage->write($name, array(
+    $original_name_data = array(
       'add_me' => 'new value',
-    ));
-    $file_storage->write($dynamic_name, array(
+    );
+    $state->write($name, $original_name_data);
+    $original_dynamic_data = array(
       'id' => 'new',
       'label' => 'New',
-    ));
-    $this->assertIdentical($file_storage->exists($name), TRUE, $name . ' found.');
-    $this->assertIdentical($file_storage->exists($dynamic_name), TRUE, $dynamic_name . ' found.');
+    );
+    $state->write($dynamic_name, $original_dynamic_data);
+    $this->assertIdentical($state->exists($name), TRUE, $name . ' found.');
+    $this->assertIdentical($state->exists($dynamic_name), TRUE, $dynamic_name . ' found.');
 
     // Import.
     config_import();
 
     // Verify the values appeared.
     $config = config($name);
-    $this->assertIdentical($config->get('add_me'), 'new value');
+    $this->assertIdentical($config->get('add_me'), $original_name_data['add_me']);
     $config = config($dynamic_name);
-    $this->assertIdentical($config->get('label'), 'New');
+    $this->assertIdentical($config->get('label'), $original_dynamic_data['label']);
   }
 
   /**
@@ -108,21 +112,29 @@ function testNew() {
   function testUpdated() {
     $name = 'config_test.system';
     $dynamic_name = 'config_test.dynamic.default';
+    $storage = $this->container->get('config.storage');
+    $state = $this->container->get('config.state');
 
     // Export.
     config_export();
 
+    // Verify that the configuration objects to import exist.
+    $this->assertIdentical($storage->exists($name), TRUE, $name . ' found.');
+    $this->assertIdentical($storage->exists($dynamic_name), TRUE, $dynamic_name . ' found.');
+
+    $this->assertIdentical($state->exists($name), TRUE, $name . ' found.');
+    $this->assertIdentical($state->exists($dynamic_name), TRUE, $dynamic_name . ' found.');
+
     // Replace the file content of the existing configuration objects.
-    $file_storage = new FileStorage();
-    $this->assertIdentical($file_storage->exists($name), TRUE, $name . ' found.');
-    $this->assertIdentical($file_storage->exists($dynamic_name), TRUE, $dynamic_name . ' found.');
-    $file_storage->write($name, array(
+    $original_name_data = array(
       'foo' => 'beer',
-    ));
-    $file_storage->write($dynamic_name, array(
+    );
+    $state->write($name, $original_name_data);
+    $original_dynamic_data = array(
       'id' => 'default',
       'label' => 'Updated',
-    ));
+    );
+    $state->write($dynamic_name, $original_dynamic_data);
 
     // Verify the active store still returns the default values.
     $config = config($name);
@@ -138,5 +150,9 @@ function testUpdated() {
     $this->assertIdentical($config->get('foo'), 'beer');
     $config = config($dynamic_name);
     $this->assertIdentical($config->get('label'), 'Updated');
+
+    // Verify that the original file content is still the same.
+    $this->assertIdentical($state->read($name), $original_name_data);
+    $this->assertIdentical($state->read($dynamic_name), $original_dynamic_data);
   }
 }
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php
new file mode 100644
index 0000000..de0ccb2
--- /dev/null
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php
@@ -0,0 +1,150 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\config\Tests\ConfigImportUITest.
+ */
+
+namespace Drupal\config\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests importing configuration from files into active store.
+ */
+class ConfigImportUITest extends WebTestBase {
+
+  public static $modules = array('config', 'config_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Import/Export UI',
+      'description' => 'Tests the user interface for importing/exporting configuration.',
+      'group' => 'Configuration',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    $this->web_user = $this->drupalCreateUser(array('synchronize configuration'));
+    $this->drupalLogin($this->web_user);
+  }
+
+  /**
+   * Tests exporting configuration.
+   */
+  function testExport() {
+    $name = 'config_test.system';
+    $dynamic_name = 'config_test.dynamic.default';
+
+    // Verify the default configuration values exist.
+    $config = config($name);
+    $this->assertIdentical($config->get('foo'), 'bar');
+    $config = config($dynamic_name);
+    $this->assertIdentical($config->get('id'), 'default');
+
+    // Verify that both appear as deleted by default.
+    $this->drupalGet('admin/config/development/sync/export');
+    $this->assertText($name);
+    $this->assertText($dynamic_name);
+
+    // Export and verify that both do not appear anymore.
+    $this->drupalPost(NULL, array(), t('Export'));
+    $this->assertUrl('admin/config/development/sync/export');
+    $this->assertNoText($name);
+    $this->assertNoText($dynamic_name);
+
+    // Verify that there are no further changes to export.
+    $this->assertText(t('There are no configuration changes.'));
+
+    // Verify that the import screen shows no changes either.
+    $this->drupalGet('admin/config/development/sync');
+    $this->assertText(t('There are no configuration changes.'));
+  }
+
+  /**
+   * Tests importing configuration.
+   */
+  function testImport() {
+    $name = 'config_test.new';
+    $dynamic_name = 'config_test.dynamic.new';
+    $storage = $this->container->get('config.storage');
+    $state = $this->container->get('config.state');
+
+    // Verify the configuration to create does not exist yet.
+    $this->assertIdentical($storage->exists($name), FALSE, $name . ' not found.');
+    $this->assertIdentical($storage->exists($dynamic_name), FALSE, $dynamic_name . ' not found.');
+    $this->assertIdentical($state->exists($name), FALSE, $name . ' not found.');
+    $this->assertIdentical($state->exists($dynamic_name), FALSE, $dynamic_name . ' not found.');
+
+    // Verify that the import UI does not allow to import without exported
+    // configuration.
+    $this->drupalGet('admin/config/development/sync');
+    $this->assertText('There is no base configuration.');
+
+    // Verify that the Export link yields to the export UI page, and export.
+    $this->clickLink('Export');
+    $this->drupalPost(NULL, array(), t('Export'));
+
+    // Create new configuration objects.
+    $original_name_data = array(
+      'add_me' => 'new value',
+    );
+    $state->write($name, $original_name_data);
+    $original_dynamic_data = array(
+      'id' => 'new',
+      'label' => 'New',
+    );
+    $state->write($dynamic_name, $original_dynamic_data);
+    $this->assertIdentical($state->exists($name), TRUE, $name . ' found.');
+    $this->assertIdentical($state->exists($dynamic_name), TRUE, $dynamic_name . ' found.');
+
+    // Verify that both appear as new.
+    $this->drupalGet('admin/config/development/sync');
+    $this->assertText($name);
+    $this->assertText($dynamic_name);
+
+    // Import and verify that both do not appear anymore.
+    $this->drupalPost(NULL, array(), t('Import'));
+    $this->assertUrl('admin/config/development/sync');
+    $this->assertNoText($name);
+    $this->assertNoText($dynamic_name);
+
+    // Verify that there are no further changes to import.
+    $this->assertText(t('There are no configuration changes.'));
+
+    // Verify that the export screen shows no changes either.
+    $this->drupalGet('admin/config/development/sync/export');
+    $this->assertText(t('There are no configuration changes.'));
+  }
+
+  /**
+   * Tests concurrent importing of configuration.
+   */
+  function testImportLock() {
+    $name = 'config_test.new';
+    $state = $this->container->get('config.state');
+
+    // Write a configuration object to import.
+    $state->write($name, array(
+      'add_me' => 'new value',
+    ));
+
+    // Verify that there are configuration differences to import.
+    $this->drupalGet('admin/config/development/sync');
+    $this->assertNoText(t('There are no configuration changes.'));
+
+    // Acquire a fake-lock on the import mechanism.
+    $lock_name = 'config_import';
+    lock_acquire($lock_name);
+
+    // Attempt to import configuration and verify that an error message appears.
+    $this->drupalPost(NULL, array(), t('Import'));
+    $this->assertUrl('admin/config/development/sync');
+    $this->assertText(t('The import failed due to an error. Any errors have been logged.'));
+
+    // Release the lock, just to keep testing sane.
+    lock_release($lock_name);
+  }
+}
diff --git a/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php
index 2dbc627..e1d77be 100644
--- a/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php
+++ b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php
@@ -32,6 +32,9 @@
   function testCRUD() {
     $name = 'config_test.storage';
 
+    // Checking whether a non-existing name exists returns FALSE.
+    $this->assertIdentical($this->storage->exists($name), FALSE);
+
     // Reading a non-existing name returns FALSE.
     $data = $this->storage->read($name);
     $this->assertIdentical($data, FALSE);
@@ -51,9 +54,13 @@ function testCRUD() {
     $data = array('foo' => 'bar');
     $result = $this->storage->write($name, $data);
     $this->assertIdentical($result, TRUE);
+
     $raw_data = $this->read($name);
     $this->assertIdentical($raw_data, $data);
 
+    // Checking whether an existing name exists returns TRUE.
+    $this->assertIdentical($this->storage->exists($name), TRUE);
+
     // Writing the identical data again still returns TRUE.
     $result = $this->storage->write($name, $data);
     $this->assertIdentical($result, TRUE);
diff --git a/core/modules/config/lib/Drupal/config/Tests/Storage/DatabaseStorageTest.php b/core/modules/config/lib/Drupal/config/Tests/Storage/DatabaseStorageTest.php
index 2e1599f..185921f 100644
--- a/core/modules/config/lib/Drupal/config/Tests/Storage/DatabaseStorageTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/Storage/DatabaseStorageTest.php
@@ -23,8 +23,34 @@ public static function getInfo() {
 
   function setUp() {
     parent::setUp();
+
+    $schema['config'] = array(
+      'description' => 'Default active store for the configuration system.',
+      'fields' => array(
+        'name' => array(
+          'description' => 'The identifier for the configuration entry, such as module.example (the name of the file, minus the file extension).',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'data' => array(
+          'description' => 'The raw data for this configuration entry.',
+          'type' => 'blob',
+          'not null' => TRUE,
+          'size' => 'big',
+          'translatable' => TRUE,
+        ),
+      ),
+      'primary key' => array('name'),
+    );
+    db_create_table('config', $schema['config']);
+
     $this->storage = new DatabaseStorage();
     $this->invalidStorage = new DatabaseStorage(array('connection' => 'invalid'));
+
+    // ::listAll() verifications require other configuration data to exist.
+    $this->storage->write('system.performance', array());
   }
 
   protected function read($name) {
diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchExcerptTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchExcerptTest.php
index b96049e..99fd422 100644
--- a/core/modules/search/lib/Drupal/search/Tests/SearchExcerptTest.php
+++ b/core/modules/search/lib/Drupal/search/Tests/SearchExcerptTest.php
@@ -7,12 +7,14 @@
 
 namespace Drupal\search\Tests;
 
-use Drupal\simpletest\UnitTestBase;
+use Drupal\simpletest\WebTestBase;
 
 /**
  * Tests the search_excerpt() function.
  */
-class SearchExcerptTest extends UnitTestBase {
+class SearchExcerptTest extends WebTestBase {
+  public static $modules = array('search');
+
   public static function getInfo() {
     return array(
       'name' => 'Search excerpt extraction',
@@ -21,11 +23,6 @@ public static function getInfo() {
     );
   }
 
-  function setUp() {
-    drupal_load('module', 'search');
-    parent::setUp();
-  }
-
   /**
    * Tests search_excerpt() with several simulated search keywords.
    *
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index f58e1de..8f1fbd6 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -716,13 +716,22 @@ protected function prepareEnvironment() {
     file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY);
     $this->generatedTestFiles = FALSE;
 
-    // Create and set a new configuration directory and signature key.
+    // Set a new configuration directory name.
     // The child site automatically adjusts the global $config_directory_name to
     // a test-prefix-specific directory within the public files directory.
     // @see config_get_config_directory()
     $GLOBALS['config_directory_name'] = 'simpletest/' . substr($this->databasePrefix, 10) . '/config';
     $this->configFileDirectory = $this->originalFileDirectory . '/' . $GLOBALS['config_directory_name'];
-    file_prepare_directory($this->configFileDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+
+    // Reset and create a new service container.
+    drupal_container(NULL, TRUE);
+    $this->container = drupal_container();
+
+    // Create the new configuration directories.
+    include_once DRUPAL_ROOT . '/core/includes/install.inc';
+    if (!install_ensure_config_directory()) {
+      return FALSE;
+    }
 
     // Log fatal errors.
     ini_set('log_errors', 1);
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php
index d97d7ae..2fa8813 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php
@@ -254,8 +254,7 @@ protected function performUpgrade($register_errors = TRUE) {
     // Reload module list. For modules that are enabled in the test database,
     // but not on the test client, we need to load the code here.
     system_list_reset();
-    $new_modules = array_diff(module_list(), $this->loadedModules);
-    foreach ($new_modules as $module) {
+    foreach (module_list() as $module) {
       drupal_load('module', $module);
     }
 
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 0e42214..70eff85 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -723,6 +723,8 @@ function system_schema() {
   );
   $schema['cache_bootstrap'] = $schema['cache'];
   $schema['cache_bootstrap']['description'] = 'Cache table for data required to bootstrap Drupal, may be routed to a shared memory cache.';
+  $schema['cache_config'] = $schema['cache'];
+  $schema['cache_config']['description'] = 'Cache table for configuration data.';
   $schema['cache_form'] = $schema['cache'];
   $schema['cache_form']['description'] = 'Cache table for the form system to store recently built forms and their storage data, to be used in subsequent page requests.';
   $schema['cache_page'] = $schema['cache'];
@@ -732,28 +734,6 @@ function system_schema() {
   $schema['cache_path'] = $schema['cache'];
   $schema['cache_path']['description'] = 'Cache table for path alias lookup.';
 
-  $schema['config'] = array(
-    'description' => 'Default active store for the configuration system.',
-    'fields' => array(
-      'name' => array(
-        'description' => 'The identifier for the configuration entry, such as module.example (the name of the file, minus the file extension).',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'data' => array(
-        'description' => 'The raw data for this configuration entry.',
-        'type' => 'blob',
-        'not null' => TRUE,
-        'size' => 'big',
-        'translatable' => TRUE,
-      ),
-    ),
-    'primary key' => array('name'),
-  );
-
-
   $schema['date_format_type'] = array(
     'description' => 'Stores configured date format types.',
     'fields' => array(
@@ -1793,6 +1773,61 @@ function system_update_8003() {
     ),
     'primary key' => array('name'),
   ));
+
+  $schema['cache'] = array(
+    'description' => 'Generic cache table for caching things not separated out into their own tables. Contributed modules may also use this to store cached items.',
+    'fields' => array(
+      'cid' => array(
+        'description' => 'Primary Key: Unique cache ID.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'data' => array(
+        'description' => 'A collection of data to cache.',
+        'type' => 'blob',
+        'not null' => FALSE,
+        'size' => 'big',
+      ),
+      'expire' => array(
+        'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'created' => array(
+        'description' => 'A Unix timestamp indicating when the cache entry was created.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'serialized' => array(
+        'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'tags' => array(
+        'description' => 'Space-separated list of cache tags for this entry.',
+        'type' => 'text',
+        'size' => 'big',
+        'not null' => FALSE,
+      ),
+      'checksum' => array(
+        'description' => 'The tag invalidation sum when this entry was saved.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'indexes' => array(
+      'expire' => array('expire'),
+    ),
+    'primary key' => array('cid'),
+  );
+  db_create_table('cache_config', $schema['cache']);
 }
 
 /**
@@ -1872,7 +1907,7 @@ function system_update_8007() {
   foreach ($tables as $table) {
     // Assume we have a valid cache table if there is both 'cid' and 'data'
     // columns.
-    if (db_field_exists($table, 'cid') && db_field_exists($table, 'data')) {
+    if (db_field_exists($table, 'cid') && db_field_exists($table, 'data') && !db_field_exists($table, 'tags')) {
       db_add_field($table, 'tags', array(
         'description' => 'Space-separated list of cache tags for this entry.',
         'type' => 'text',
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 983c317..fe6e5f36 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -3398,7 +3398,7 @@ function system_cron() {
  */
 function system_cache_flush() {
   // Do NOT flush the 'form' cache bin to retain in-progress form submissions.
-  return array('bootstrap', 'cache', 'page', 'path');
+  return array('bootstrap', 'config', 'cache', 'page', 'path');
 }
 
 /**
diff --git a/core/profiles/standard/standard.info b/core/profiles/standard/standard.info
index 8b8a33b..c5240dd 100644
--- a/core/profiles/standard/standard.info
+++ b/core/profiles/standard/standard.info
@@ -6,6 +6,7 @@ dependencies[] = node
 dependencies[] = block
 dependencies[] = color
 dependencies[] = comment
+dependencies[] = config
 dependencies[] = contextual
 dependencies[] = help
 dependencies[] = image
