diff --git a/core/core.services.yml b/core/core.services.yml
index 24fa98e..84b1fa3 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -146,7 +146,7 @@ services:
     arguments: ['@config.storage', '@event_dispatcher', '@config.typed']
   config.installer:
     class: Drupal\Core\Config\ConfigInstaller
-    arguments: ['@config.factory', '@config.storage', '@config.typed', '@config.manager', '@event_dispatcher']
+    arguments: ['@config.factory', '@config.storage', '@config.typed', '@config.manager', '@event_dispatcher', '@logger.channel.default']
   config.storage:
     class: Drupal\Core\Config\CachedStorage
     arguments: ['@config.storage.active', '@cache.config']
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 0358b39..632ea59 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -741,6 +741,8 @@ function install_tasks($install_state) {
     ),
     'install_profile_themes' => array(
     ),
+    'install_install_profile' => array(
+    ),
     'install_import_translations' => array(
       'display_name' => t('Set up translations'),
       'display' => $needs_translations,
@@ -1022,10 +1024,6 @@ function install_base_system(&$install_state) {
   // State can be set to the database now that system.module is installed.
   $modules = $install_state['profile_info']['dependencies'];
 
-  // The installation profile is also a module, which needs to be installed
-  // after all the dependencies have been installed.
-  $modules[] = drupal_get_profile();
-
   \Drupal::state()->set('install_profile_modules', array_diff($modules, array('system')));
   $install_state['base_system_verified'] = TRUE;
 }
@@ -1571,10 +1569,7 @@ function install_profile_modules(&$install_state) {
   // the modules.
   $required = array();
   $non_required = array();
-  // Although the profile module is marked as required, it needs to go after
-  // every dependency, including non-required ones. So clear its required
-  // flag for now to allow it to install late.
-  $files[$install_state['parameters']['profile']]->info['required'] = FALSE;
+
   // Add modules that other modules depend on.
   foreach ($modules as $module) {
     if ($files[$module]->requires) {
@@ -1634,6 +1629,19 @@ function install_profile_themes(&$install_state) {
 }
 
 /**
+ * Installs the install profile.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ */
+function install_install_profile(&$install_state) {
+  \Drupal::service('module_installer')->install(array(drupal_get_profile()), FALSE);
+  // Install all available optional config.
+  // @todo discuss whether this is the desired approach?
+  \Drupal::service('config.installer')->installAllOptionalConfig();
+}
+
+/**
  * Prepares the system for import and downloads additional translations.
  *
  * @param $install_state
diff --git a/core/lib/Drupal/Core/Config/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php
index 8065613..9d0f81d 100644
--- a/core/lib/Drupal/Core/Config/ConfigInstaller.php
+++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php
@@ -9,7 +9,9 @@
 
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Config\Entity\ConfigDependencyManager;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Site\Settings;
+use Psr\Log\LoggerInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 class ConfigInstaller implements ConfigInstallerInterface {
@@ -57,6 +59,13 @@ class ConfigInstaller implements ConfigInstallerInterface {
   protected $sourceStorage;
 
   /**
+   * The logger.
+   *
+   * @var \Psr\Log\LoggerInterface
+   */
+  protected $logger;
+
+  /**
    * Is configuration being created as part of a configuration sync.
    *
    * @var bool
@@ -76,13 +85,16 @@ class ConfigInstaller implements ConfigInstallerInterface {
    *   The configuration manager.
    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
    *   The event dispatcher.
+   * @param \Psr\Log\LoggerInterface $logger
+   *   The logger.
    */
-  public function __construct(ConfigFactoryInterface $config_factory, StorageInterface $active_storage, TypedConfigManagerInterface $typed_config, ConfigManagerInterface $config_manager, EventDispatcherInterface $event_dispatcher) {
+  public function __construct(ConfigFactoryInterface $config_factory, StorageInterface $active_storage, TypedConfigManagerInterface $typed_config, ConfigManagerInterface $config_manager, EventDispatcherInterface $event_dispatcher, LoggerInterface $logger) {
     $this->configFactory = $config_factory;
     $this->activeStorages[$active_storage->getCollectionName()] = $active_storage;
     $this->typedConfig = $typed_config;
     $this->configManager = $config_manager;
     $this->eventDispatcher = $event_dispatcher;
+    $this->logger = $logger;
   }
 
   /**
@@ -96,34 +108,106 @@ public function installDefaultConfig($type, $name) {
       $this->typedConfig->clearCachedDefinitions();
     }
 
-    // Gather information about all the supported collections.
-    $collection_info = $this->configManager->getConfigCollectionInfo();
+    $default_install_path = $this->getDefaultConfigDirectory($type, $name);
+    if (is_dir($default_install_path)) {
+      if (!$this->isSyncing()) {
+        // Set the source storage to the configuration provided by the extension.
+        $this->setSourceStorage(new FileStorage($default_install_path, StorageInterface::DEFAULT_COLLECTION));
+        $prefix = NULL;
+      }
+      else {
+        // The configuration importer sets the source storage on the config
+        // installer. The configuration importer handles all of the
+        // configuration entity imports. We only need to ensure that simple
+        // configuration is created when the extension is installed.
+        $prefix = $name;
+      }
 
-    // Read enabled extensions directly from configuration to avoid circular
-    // dependencies with ModuleHandler and ThemeHandler.
-    $extension_config = $this->configFactory->get('core.extension');
-    $modules = (array) $extension_config->get('module');
-    // Unless we are installing the profile, remove it from the list.
-    if ($install_profile = Settings::get('install_profile')) {
-      if ($name !== $install_profile) {
-        unset($modules[$install_profile]);
+      // Gather information about all the supported collections.
+      $collection_info = $this->configManager->getConfigCollectionInfo();
+      foreach ($collection_info->getCollectionNames(TRUE) as $collection) {
+        $config_to_install = $this->getDefaultConfigToInstall($collection, $prefix);
+        if (!empty($config_to_install)) {
+          $this->createConfiguration($collection, $config_to_install);
+        }
       }
     }
-    $enabled_extensions = array_keys($modules);
-    $enabled_extensions += array_keys((array) $extension_config->get('theme'));
-
-    // Core can provide configuration.
-    $enabled_extensions[] = 'core';
 
-    foreach ($collection_info->getCollectionNames(TRUE) as $collection) {
-      $config_to_install = $this->listDefaultConfigToInstall($type, $name, $collection, $enabled_extensions);
-      if (!empty($config_to_install)) {
-        $this->createConfiguration($collection, $config_to_install);
+    if (!$this->isSyncing() && !drupal_installation_attempted()) {
+      $optional_install_path = $extension_path . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
+      if (is_dir($optional_install_path)) {
+        // Install any optional config the module provides.
+        $this->setSourceStorage(new FileStorage($optional_install_path, StorageInterface::DEFAULT_COLLECTION));
+        $this->installOptionalConfig();
+      }
+      // Install any optional configuration entities whose type this extension
+      // provides. This searches all the installed modules config/optional
+      // directories.
+      $provides_config_entity_type = array_reduce($this->configManager->getEntityManager()->getDefinitions(), function ($return, EntityTypeInterface $entity_type) use ($name) {
+        return $return ?: $entity_type->getProvider() && $entity_type->getConfigPrefix();
+      }, FALSE);
+      if ($provides_config_entity_type) {
+        $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, FALSE);
+        $this->setSourceStorage($storage);
+        $this->installOptionalConfig($name);
       }
     }
 
     // Reset all the static caches and list caches.
     $this->configFactory->reset();
+    $this->resetSourceStorage();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function installAllOptionalConfig() {
+    $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, drupal_installation_attempted());
+    $this->setSourceStorage($storage);
+    $this->installOptionalConfig();
+    $this->resetSourceStorage();
+  }
+
+  /**
+   * Installs optional configuration.
+   *
+   * Optional configuration is only installed if:
+   * - the configuration does not exist already.
+   * - it's a configuration entity.
+   * - its dependencies can be met.
+   *
+   * @param string $name
+   *   If set, limits the installed configuration to only configuration
+   *   beginning with the provided value.
+   */
+  protected function installOptionalConfig($name = NULL) {
+    if ($this->isSyncing()) {
+      return;
+    }
+    $collection_info = $this->configManager->getConfigCollectionInfo();
+    $enabled_extensions = $this->getEnabledExtensions();
+    $existing_config = $this->configFactory->listAll();
+    foreach ($collection_info->getCollectionNames(TRUE) as $collection) {
+      $config_entity_support = $this->configManager->supportsConfigurationEntities($collection);
+      if (!$config_entity_support) {
+        continue;
+      }
+
+      $config_to_install = $this->getDefaultConfigToInstall($collection, $name);
+      $all_config = array_merge($existing_config, array_keys($config_to_install));
+      foreach ($config_to_install as $config_name => $data) {
+        // Ensure that the configuration does not exist already, that it is a
+        // configuration entity, and that its dependencies can be met.
+        if (in_array($config_name, $existing_config) ||
+          !$this->configManager->getEntityTypeIdByName($config_name) ||
+          !$this->validateDependencies($config_name, $data, $enabled_extensions, $all_config)) {
+          unset($config_to_install[$config_name]);
+        }
+      }
+      if (!empty($config_to_install)) {
+        $this->createConfiguration($collection, $config_to_install, TRUE);
+      }
+    }
   }
 
   /**
@@ -133,43 +217,23 @@ public function installDefaultConfig($type, $name) {
    * currently enabled extensions config/install directories for configuration
    * that begins with the extension's name.
    *
-   * @param string $type
-   *   The extension type; e.g., 'module' or 'theme'.
-   * @param string $name
-   *   The name of the module or theme to install default configuration for.
    * @param string $collection
    *  The configuration collection to install.
-   * @param array $enabled_extensions
-   *   A list of all the currently enabled modules and themes.
+   * @param string $name
+   *   The name that configuration must start with.
+
    *
    * @return array
    *   The list of configuration objects to create.
    */
-  protected function listDefaultConfigToInstall($type, $name, $collection, array $enabled_extensions) {
-    // Get all default configuration owned by this extension.
-    $source_storage = $this->getSourceStorage($collection);
-    $config_to_install = $source_storage->listAll($name . '.');
-
-    // If not installing the core base system default configuration, work out if
-    // this extension provides default configuration for any other enabled
-    // extensions.
-    $extension_path = drupal_get_path($type, $name);
-    if ($type !== 'core' && is_dir($extension_path . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY)) {
-      $default_storage = new FileStorage($extension_path . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection);
-      $extension_provided_config = array_filter($default_storage->listAll(), function ($config_name) use ($config_to_install, $enabled_extensions) {
-        // Ensure that we have not already discovered the config to install.
-        if (in_array($config_name, $config_to_install)) {
-          return FALSE;
-        }
-        // Ensure the configuration is provided by an enabled module.
-        $provider = Unicode::substr($config_name, 0, strpos($config_name, '.'));
-        return in_array($provider, $enabled_extensions);
-      });
-
-      $config_to_install = array_merge($config_to_install, $extension_provided_config);
+  protected function getDefaultConfigToInstall($collection, $name = NULL) {
+    $prefix = '';
+    if ($name) {
+      $prefix = $name . '.';
     }
-
-    return $config_to_install;
+    $storage = $this->getSourceStorage($collection);
+    $list = $storage->listAll($prefix);
+    return $storage->readMultiple($list);
   }
 
   /**
@@ -177,12 +241,11 @@ protected function listDefaultConfigToInstall($type, $name, $collection, array $
    *
    * @param string $collection
    *   The configuration collection.
-   * @param array $config_to_install
-   *   A list of configuration object names to create.
+   * @param array $data
+   *   An array of configuration data to create, keyed by name.
    */
-  protected function createConfiguration($collection, array $config_to_install) {
+  protected function createConfiguration($collection, array $data) {
     // Order the configuration to install in the order of dependencies.
-    $data = $this->getSourceStorage($collection)->readMultiple($config_to_install);
     $config_entity_support = $this->configManager->supportsConfigurationEntities($collection);
     if ($config_entity_support) {
       $dependency_manager = new ConfigDependencyManager();
@@ -190,9 +253,9 @@ protected function createConfiguration($collection, array $config_to_install) {
         ->setData($data)
         ->sortAll();
     }
-
-    // Remove configuration that already exists in the active storage.
-    $config_to_install = array_diff($config_to_install, $this->getActiveStorages($collection)->listAll());
+    else {
+      $config_to_install = array_keys($data);
+    }
 
     foreach ($config_to_install as $name) {
       // Allow config factory overriders to use a custom configuration object if
@@ -208,14 +271,13 @@ protected function createConfiguration($collection, array $config_to_install) {
         $new_config->setData($data[$name]);
       }
       if ($config_entity_support && $entity_type = $this->configManager->getEntityTypeIdByName($name)) {
-
         // If we are syncing do not create configuration entities. Pluggable
         // configuration entities can have dependencies on modules that are
         // not yet enabled. This approach means that any code that expects
         // default configuration entities to exist will be unstable after the
         // module has been enabled and before the config entity has been
         // imported.
-        if ($this->isSyncing) {
+        if ($this->isSyncing()) {
           continue;
         }
         /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $entity_storage */
@@ -246,16 +308,15 @@ protected function createConfiguration($collection, array $config_to_install) {
    * {@inheritdoc}
    */
   public function installCollectionDefaultConfig($collection) {
-    $config_to_install = $this->getSourceStorage($collection)->listAll();
-    $extension_config = $this->configFactory->get('core.extension');
-    $enabled_extensions = array_keys((array) $extension_config->get('module'));
-    $enabled_extensions += array_keys((array) $extension_config->get('theme'));
-    $config_to_install = array_filter($config_to_install, function ($config_name) use ($enabled_extensions) {
+    $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, drupal_installation_attempted());
+    // Only install configuration for enabled extensions.
+    $enabled_extensions = $this->getEnabledExtensions();
+    $config_to_install = array_filter($storage->listAll(), function ($config_name) use ($enabled_extensions) {
       $provider = Unicode::substr($config_name, 0, strpos($config_name, '.'));
       return in_array($provider, $enabled_extensions);
     });
     if (!empty($config_to_install)) {
-      $this->createConfiguration($collection, $config_to_install);
+      $this->createConfiguration($collection, $storage->readMultiple($config_to_install));
       // Reset all the static caches and list caches.
       $this->configFactory->reset();
     }
@@ -289,10 +350,11 @@ public function resetSourceStorage() {
    */
   public function getSourceStorage($collection = StorageInterface::DEFAULT_COLLECTION) {
     if (!isset($this->sourceStorage)) {
+      throw new ConfigException('This should not happen');
       // Default to using the ExtensionInstallStorage which searches extension's
       // config directories for default configuration. Only include the profile
       // configuration during Drupal installation.
-      $this->sourceStorage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, drupal_installation_attempted());
+      // $this->sourceStorage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, drupal_installation_attempted());
     }
     if ($this->sourceStorage->getCollectionName() != $collection) {
       $this->sourceStorage = $this->sourceStorage->createCollection($collection);
@@ -333,22 +395,27 @@ public function isSyncing() {
   }
 
   /**
-   * {@inheritdoc}
+   * Finds pre-existing configuration objects for the provided extension.
+   *
+   * Extensions can not be installed if configuration objects exist in the
+   * active storage with the same names. This can happen in a number of ways,
+   * commonly:
+   * - if a user has created configuration with the same name as that provided
+   *   by the extension.
+   * - if the extension provides default configuration that does not depend on
+   *   it and the extension has been uninstalled and is about to the
+   *   reinstalled.
+   *
+   * @return array
+   *   Array of configuration objects that already exist keyed by collection.
    */
-  public function findPreExistingConfiguration($type, $name) {
+  protected function findPreExistingConfiguration() {
     $existing_configuration = array();
     // Gather information about all the supported collections.
     $collection_info = $this->configManager->getConfigCollectionInfo();
 
-    // Read enabled extensions directly from configuration to avoid circular
-    // dependencies on ModuleHandler and ThemeHandler.
-    $extension_config = $this->configFactory->get('core.extension');
-    $enabled_extensions = array_keys((array) $extension_config->get('module'));
-    $enabled_extensions += array_keys((array) $extension_config->get('theme'));
-    // Add the extension that will be enabled to the list of enabled extensions.
-    $enabled_extensions[] = $name;
     foreach ($collection_info->getCollectionNames(TRUE) as $collection) {
-      $config_to_install = $this->listDefaultConfigToInstall($type, $name, $collection, $enabled_extensions);
+      $config_to_install = array_keys($this->getDefaultConfigToInstall($collection));
       $active_storage = $this->getActiveStorages($collection);
       foreach ($config_to_install as $config_name) {
         if ($active_storage->exists($config_name)) {
@@ -358,4 +425,184 @@ public function findPreExistingConfiguration($type, $name) {
     }
     return $existing_configuration;
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function checkConfigurationToInstall($type, $name) {
+    if ($this->isSyncing()) {
+      // Configuration is assumed to already be checked by the config importer
+      // validation events
+      return;
+    }
+    $config_install_path = $this->getDefaultConfigDirectory($type, $name);
+    if (!is_dir($config_install_path)) {
+      return;
+    }
+
+    $this->setSourceStorage(new FileStorage($config_install_path, StorageInterface::DEFAULT_COLLECTION));
+
+    $enabled_extensions = $this->getEnabledExtensions();
+    // Add the extension that will be enabled to the list of enabled extensions.
+    $enabled_extensions[] = $name;
+
+    // Check the dependencies of configuration provided by the module.
+    $invalid_default_config = $this->findDefaultConfigWithUnmetDependencies($enabled_extensions);
+    if (!empty($invalid_default_config)) {
+      throw UnmetDependenciesException::create($name, $invalid_default_config);
+    }
+
+    // Install profiles can not have config clashes. Configuration that
+    // has the same name as a module's configuration will be used instead.
+    if ($name != $this->drupalGetProfile()) {
+      // Throw an exception if the module being installed contains configuration
+      // that already exits. Additionally, can not continue installing more
+      // modules because those may depend on the current module being installed.
+      $existing_configuration = $this->findPreExistingConfiguration();
+      if (!empty($existing_configuration)) {
+        throw PreExistingConfigException::create($name, $existing_configuration);
+      }
+    }
+  }
+
+  /**
+   * Finds default configuration with unmet dependencies.
+   *
+   * @param array $enabled_extensions
+   *   A list of all the currently enabled modules and themes.
+   *
+   * @return array
+   *   List of configuration that has unmet dependencies
+   */
+  protected function findDefaultConfigWithUnmetDependencies(array $enabled_extensions) {
+    $config_data = $this->getDefaultConfigToInstall(StorageInterface::DEFAULT_COLLECTION);
+    $all_config = array_merge($this->configFactory->listAll(), array_keys($config_data));
+    return array_filter(array_keys($config_data), function($config_name) use ($enabled_extensions, $all_config, $config_data) {
+      return !$this->validateDependencies($config_name, $config_data[$config_name], $enabled_extensions, $all_config);
+    });
+  }
+
+  /**
+   * Validates an array of config data that contains dependency information.
+   *
+   * @param string $config_name
+   *   The name of the configuration object that is being validated.
+   * @param array $data
+   *   Configuration data.
+   * @param array $enabled_extensions
+   *   A list of all the currently enabled modules and themes.
+   * @param array $all_config
+   *   A list of all the active configuration names.
+   *
+   * @return bool
+   *   TRUE if the dependencies are met, FALSE if not.
+   */
+  protected function validateDependencies($config_name, array $data, array $enabled_extensions, array $all_config) {
+    // All the migrate tests will fail if we check since they install the
+    // migrate_drupal module but only set up the dependencies for the single
+    // migration they are testing.
+    if (strpos($config_name, 'migrate.migration.') === 0) {
+      return TRUE;
+    }
+    if (isset($data['dependencies'])) {
+      $all_dependencies = $data['dependencies'];
+
+      // Ensure enforced dependencies are included.
+      if (isset($all_dependencies['enforced'])) {
+        $all_dependencies = array_merge($all_dependencies, $data['dependencies']['enforced']);
+      }
+      // Ensure the configuration entity type provider is in the list of
+      // dependencies.
+      list($provider) = explode('.', $config_name, 2);
+      if (!isset($all_dependencies['module'])) {
+        $all_dependencies['module'][] = $provider;
+      }
+      elseif (!in_array($provider, $all_dependencies['module'])) {
+        $all_dependencies['module'][] = $provider;
+      }
+
+      foreach ($all_dependencies as $type => $dependencies) {
+        $list_to_check = [];
+        switch ($type) {
+          case 'module':
+          case 'theme':
+            $list_to_check = $enabled_extensions;
+            break;
+          case 'config':
+            $list_to_check = $all_config;
+            break;
+        }
+        if (!empty($list_to_check)) {
+          $missing = array_diff($dependencies, $list_to_check);
+          if (!empty($missing)) {
+            return FALSE;
+          }
+        }
+      }
+    }
+    return TRUE;
+  }
+
+  /**
+   * Gets the list of enabled extensions including both modules and themes.
+   *
+   * @return array
+   *   A list of enabled extensions which includes both modules and themes.
+   */
+  protected function getEnabledExtensions() {
+    // Read enabled extensions directly from configuration to avoid circular
+    // dependencies on ModuleHandler and ThemeHandler.
+    $extension_config = $this->configFactory->get('core.extension');
+    $enabled_extensions = (array) $extension_config->get('module');
+    $enabled_extensions += (array) $extension_config->get('theme');
+    // Core can provide configuration.
+    $enabled_extensions['core'] = 'core';
+    return array_keys($enabled_extensions);
+  }
+
+  /**
+   * Gets an extension's default configuration directory.
+   *
+   * @param string $type
+   *   Type of extension to install.
+   * @param string $name
+   *   Name of extension to install.
+   *
+   * @return string
+   *   The extension's default configuration directory.
+   */
+  protected function getDefaultConfigDirectory($type, $name) {
+    return $this->drupalGetPath($type, $name) . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY;
+  }
+
+  /**
+   * Wrapper for drupal_get_path().
+   *
+   * @param $type
+   *   The type of the item; one of 'core', 'profile', 'module', 'theme', or
+   *   'theme_engine'.
+   * @param $name
+   *   The name of the item for which the path is requested. Ignored for
+   *   $type 'core'.
+   *
+   * @return string
+   *   The path to the requested item or an empty string if the item is not
+   *   found.
+   */
+  protected function drupalGetPath($type, $name) {
+    return drupal_get_path($type, $name);
+  }
+
+  /**
+   * Wrapper for drupal_get_profile().
+   *
+   * @return string|null $profile
+   *   The name of the installation profile or NULL if no installation profile is
+   *   currently active. This is the case for example during the first steps of
+   *   the installer or during unit tests.
+   */
+  protected function drupalGetProfile() {
+    return drupal_get_profile();
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/ConfigInstallerInterface.php b/core/lib/Drupal/Core/Config/ConfigInstallerInterface.php
index 1d1e7e0..e49dfe7 100644
--- a/core/lib/Drupal/Core/Config/ConfigInstallerInterface.php
+++ b/core/lib/Drupal/Core/Config/ConfigInstallerInterface.php
@@ -38,6 +38,16 @@
   public function installDefaultConfig($type, $name);
 
   /**
+   * Installs all optional configuration provided by any extension.
+   *
+   * Optional configuration is only installed if:
+   * - the configuration does not exist already.
+   * - it's a configuration entity.
+   * - its dependencies can be met.
+   */
+  public function installAllOptionalConfig();
+
+  /**
    * Installs all default configuration in the specified collection.
    *
    * The function is useful if the site needs to respond to an event that has
@@ -85,25 +95,16 @@ public function setSyncing($status);
   public function isSyncing();
 
   /**
-   * Finds pre-existing configuration objects for the provided extension.
-   *
-   * Extensions can not be installed if configuration objects exist in the
-   * active storage with the same names. This can happen in a number of ways,
-   * commonly:
-   * - if a user has created configuration with the same name as that provided
-   *   by the extension.
-   * - if the extension provides default configuration that does not depend on
-   *   it and the extension has been uninstalled and is about to the
-   *   reinstalled.
+   * Checks the configuration that will be installed for an extension.
    *
    * @param string $type
    *   Type of extension to install.
    * @param string $name
    *   Name of extension to install.
    *
-   * @return array
-   *   Array of configuration objects that already exist keyed by collection.
+   * @throws \Drupal\Core\Config\UnmetDependenciesException
+   * @throws \Drupal\Core\Config\PreExistingConfigException
    */
-  public function findPreExistingConfiguration($type, $name);
+  public function checkConfigurationToInstall($type, $name);
 
 }
diff --git a/core/lib/Drupal/Core/Config/InstallStorage.php b/core/lib/Drupal/Core/Config/InstallStorage.php
index d4e7d5c..06618d7 100644
--- a/core/lib/Drupal/Core/Config/InstallStorage.php
+++ b/core/lib/Drupal/Core/Config/InstallStorage.php
@@ -28,6 +28,11 @@ class InstallStorage extends FileStorage {
   const CONFIG_INSTALL_DIRECTORY = 'config/install';
 
   /**
+   * Extension sub-directory containing optional configuration for installation.
+   */
+  const CONFIG_OPTIONAL_DIRECTORY = 'config/optional';
+
+  /**
    * Extension sub-directory containing configuration schema.
    */
   const CONFIG_SCHEMA_DIRECTORY = 'config/schema';
diff --git a/core/lib/Drupal/Core/Config/UnmetDependenciesException.php b/core/lib/Drupal/Core/Config/UnmetDependenciesException.php
new file mode 100644
index 0000000..db7ad9c
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/UnmetDependenciesException.php
@@ -0,0 +1,96 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Config\UnmetDependenciesException.
+ */
+
+namespace Drupal\Core\Config;
+
+use Drupal\Component\Utility\String;
+use Drupal\Core\StringTranslation\TranslationInterface;
+
+/**
+ * An exception thrown if configuration has unmet dependencies.
+ */
+class UnmetDependenciesException extends ConfigException {
+
+  /**
+   * A list of configuration objects that have unmet dependencies.
+   *
+   * @var array
+   */
+  protected $configObjects = [];
+
+  /**
+   * The name of the extension that is being installed.
+   *
+   * @var string
+   */
+  protected $extension;
+
+  /**
+   * Gets the list of configuration objects that have unmet dependencies.
+   *
+   * @return array
+   *   A list of configuration objects that have unmet dependencies.
+   */
+  public function getConfigObjects() {
+    return $this->configObjects;
+  }
+
+  /**
+   * Gets the name of the extension that is being installed.
+   *
+   * @return string
+   *   The name of the extension that is being installed.
+   */
+  public function getExtension() {
+    return $this->extension;
+  }
+
+  /**
+   * Gets a translated message from the exception.
+   *
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation service.
+   *
+   * @return string
+   */
+  public function getTranslatedMessage(TranslationInterface $string_translation, $extension) {
+    return $string_translation->formatPlural(
+      count($this->getConfigObjects()),
+      'Unable to install @extension, %config_names has unmet dependencies.',
+      'Unable to install @extension, %config_names have unmet dependencies.',
+      [
+        '%config_names' => implode(', ', $this->getConfigObjects()),
+        '@extension' => $extension,
+      ]
+    );
+  }
+
+  /**
+   * Creates an exception for an extension and a list of configuration objects.
+   *
+   * @param $extension
+   *   The name of the extension that is being installed.
+   * @param array $config_objects
+   *   A list of configuration objects that already exist in active
+   *   configuration, keyed by config collection.
+   *
+   * @return \Drupal\Core\Config\PreExistingConfigException
+   */
+  public static function create($extension, array $config_objects) {
+    $message = String::format('Configuration objects (@config_names) provided by @extension have unmet dependencies',
+      array(
+        '@config_names' => implode(', ', $config_objects),
+        '@extension' => $extension
+      )
+    );
+    $e = new static($message);
+    $e->configObjects = $config_objects;
+    $e->extension = $extension;
+    return $e;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Extension/ModuleInstaller.php b/core/lib/Drupal/Core/Extension/ModuleInstaller.php
index dba7a94..af2ab64 100644
--- a/core/lib/Drupal/Core/Extension/ModuleInstaller.php
+++ b/core/lib/Drupal/Core/Extension/ModuleInstaller.php
@@ -151,17 +151,9 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
           )));
         }
 
-        // Install profiles can not have config clashes. Configuration that
-        // has the same name as a module's configuration will be used instead.
-        if ($module != drupal_get_profile()) {
-          // Validate default configuration of this module. Bail if unable to
-          // install. Should not continue installing more modules because those
-          // may depend on this one.
-          $existing_configuration = $config_installer->findPreExistingConfiguration('module', $module);
-          if (!empty($existing_configuration)) {
-            throw PreExistingConfigException::create($module, $existing_configuration);
-          }
-        }
+        // Check the validity of the default configuration. This will throw
+        // exceptions if the configuration is not valid.
+        $config_installer->checkConfigurationToInstall('module', $module);
 
         $extension_config
           ->set("module.$module", 0)
diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php
index 9529c8a..79ca84c 100644
--- a/core/lib/Drupal/Core/Extension/ThemeHandler.php
+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -258,10 +258,7 @@ public function install(array $theme_list, $install_dependencies = TRUE) {
 
       // Validate default configuration of the theme. If there is existing
       // configuration then stop installing.
-      $existing_configuration = $this->configInstaller->findPreExistingConfiguration('theme', $key);
-      if (!empty($existing_configuration)) {
-        throw PreExistingConfigException::create($key, $existing_configuration);
-      }
+      $this->configInstaller->checkConfigurationToInstall('theme', $key);
 
       // The value is not used; the weight is ignored for themes currently.
       $extension_config
diff --git a/core/modules/aggregator/config/install/views.view.aggregator_rss_feed.yml b/core/modules/aggregator/config/optional/views.view.aggregator_rss_feed.yml
similarity index 100%
rename from core/modules/aggregator/config/install/views.view.aggregator_rss_feed.yml
rename to core/modules/aggregator/config/optional/views.view.aggregator_rss_feed.yml
diff --git a/core/modules/aggregator/config/install/views.view.aggregator_sources.yml b/core/modules/aggregator/config/optional/views.view.aggregator_sources.yml
similarity index 100%
rename from core/modules/aggregator/config/install/views.view.aggregator_sources.yml
rename to core/modules/aggregator/config/optional/views.view.aggregator_sources.yml
diff --git a/core/modules/block/src/Tests/BlockStorageUnitTest.php b/core/modules/block/src/Tests/BlockStorageUnitTest.php
index ff4ba29..eb3f8eb 100644
--- a/core/modules/block/src/Tests/BlockStorageUnitTest.php
+++ b/core/modules/block/src/Tests/BlockStorageUnitTest.php
@@ -149,6 +149,7 @@ protected function deleteTests() {
    * Tests the installation of default blocks.
    */
   public function testDefaultBlocks() {
+    \Drupal::service('theme_handler')->install(['classy']);
     $entities = $this->controller->loadMultiple();
     $this->assertTrue(empty($entities), 'There are no blocks initially.');
 
diff --git a/core/modules/block/tests/modules/block_test/block_test.info.yml b/core/modules/block/tests/modules/block_test/block_test.info.yml
index 9a8895f..fceed36 100644
--- a/core/modules/block/tests/modules/block_test/block_test.info.yml
+++ b/core/modules/block/tests/modules/block_test/block_test.info.yml
@@ -4,3 +4,5 @@ description: 'Provides test blocks.'
 package: Testing
 version: VERSION
 core: 8.x
+dependencies:
+  - block
diff --git a/core/modules/block_content/src/Tests/BlockContentPageViewTest.php b/core/modules/block_content/src/Tests/BlockContentPageViewTest.php
index 90adab7..0564c60 100644
--- a/core/modules/block_content/src/Tests/BlockContentPageViewTest.php
+++ b/core/modules/block_content/src/Tests/BlockContentPageViewTest.php
@@ -19,7 +19,7 @@ class BlockContentPageViewTest extends BlockContentTestBase {
    *
    * @var array
    */
-  public static $modules = array('block', 'block_content', 'block_content_test');
+  public static $modules = array('block_content_test');
 
   /**
    * Checks block edit and fallback functionality.
diff --git a/core/modules/block_content/tests/modules/block_content_test/block_content_test.info.yml b/core/modules/block_content/tests/modules/block_content_test/block_content_test.info.yml
index 747efa4..16e4bc7 100644
--- a/core/modules/block_content/tests/modules/block_content_test/block_content_test.info.yml
+++ b/core/modules/block_content/tests/modules/block_content_test/block_content_test.info.yml
@@ -4,3 +4,5 @@ description: "Support module for custom block related testing."
 package: Testing
 version: VERSION
 core: 8.x
+dependencies:
+  - block_content
diff --git a/core/modules/block_content/tests/modules/block_content_test/config/install/block.block.foobargorilla.yml b/core/modules/block_content/tests/modules/block_content_test/config/install/block.block.foobargorilla.yml
index ed83291..4cc2360 100644
--- a/core/modules/block_content/tests/modules/block_content_test/config/install/block.block.foobargorilla.yml
+++ b/core/modules/block_content/tests/modules/block_content_test/config/install/block.block.foobargorilla.yml
@@ -4,7 +4,7 @@ dependencies:
   module:
     - block_content
   theme:
-    - stark
+    - classy
 id: foobargorilla
 theme: classy
 region: content
diff --git a/core/modules/book/config/install/core.entity_view_mode.node.print.yml b/core/modules/book/config/install/core.entity_view_mode.node.print.yml
index 512d581..de47b0c 100644
--- a/core/modules/book/config/install/core.entity_view_mode.node.print.yml
+++ b/core/modules/book/config/install/core.entity_view_mode.node.print.yml
@@ -5,4 +5,8 @@ cache: true
 targetEntityType: node
 dependencies:
   module:
+    - book
     - node
+  enforced:
+    module:
+      - book
diff --git a/core/modules/comment/config/install/views.view.comments_recent.yml b/core/modules/comment/config/optional/views.view.comments_recent.yml
similarity index 100%
rename from core/modules/comment/config/install/views.view.comments_recent.yml
rename to core/modules/comment/config/optional/views.view.comments_recent.yml
diff --git a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
index 763d106..3df2879 100644
--- a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
+++ b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
@@ -42,7 +42,7 @@ protected function setUp() {
 
     // Install tables and config needed to render comments.
     $this->installSchema('comment', array('comment_entity_statistics'));
-    $this->installConfig(array('system', 'filter'));
+    $this->installConfig(array('system', 'filter', 'comment'));
 
     // Comment rendering generates links, so build the router.
     $this->installSchema('system', array('router'));
diff --git a/core/modules/comment/src/Tests/CommentFieldAccessTest.php b/core/modules/comment/src/Tests/CommentFieldAccessTest.php
index 78e5146..c51b47e 100644
--- a/core/modules/comment/src/Tests/CommentFieldAccessTest.php
+++ b/core/modules/comment/src/Tests/CommentFieldAccessTest.php
@@ -78,7 +78,7 @@ class CommentFieldAccessTest extends EntityUnitTestBase {
    */
   protected function setUp() {
     parent::setUp();
-    $this->installConfig(array('user'));
+    $this->installConfig(array('user', 'comment'));
     $this->installSchema('comment', array('comment_entity_statistics'));
   }
 
diff --git a/core/modules/config/src/Tests/ConfigImportRecreateTest.php b/core/modules/config/src/Tests/ConfigImportRecreateTest.php
index e1a607a..1c0166d 100644
--- a/core/modules/config/src/Tests/ConfigImportRecreateTest.php
+++ b/core/modules/config/src/Tests/ConfigImportRecreateTest.php
@@ -37,7 +37,7 @@ protected function setUp() {
     parent::setUp();
 
     $this->installEntitySchema('node');
-    $this->installConfig(array('field'));
+    $this->installConfig(array('field', 'node'));
 
     $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
 
@@ -93,8 +93,8 @@ public function testRecreateEntity() {
     // will be recreated.
     $creates = $this->configImporter->getUnprocessedConfiguration('create');
     $deletes = $this->configImporter->getUnprocessedConfiguration('delete');
-    $this->assertEqual(4, count($creates), 'There are 4 configuration items to create.');
-    $this->assertEqual(4, count($deletes), 'There are 4 configuration items to delete.');
+    $this->assertEqual(5, count($creates), 'There are 4 configuration items to create.');
+    $this->assertEqual(5, count($deletes), 'There are 4 configuration items to delete.');
     $this->assertEqual(0, count($this->configImporter->getUnprocessedConfiguration('update')), 'There are no configuration items to update.');
     $this->assertIdentical($creates, array_reverse($deletes), 'Deletes and creates contain the same configuration names in opposite orders due to dependencies.');
 
diff --git a/core/modules/config/src/Tests/ConfigInstallTest.php b/core/modules/config/src/Tests/ConfigInstallTest.php
index 2da9e53..00be940 100644
--- a/core/modules/config/src/Tests/ConfigInstallTest.php
+++ b/core/modules/config/src/Tests/ConfigInstallTest.php
@@ -48,11 +48,8 @@ function testModuleInstallation() {
     $this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install does not exist.');
 
     // Install the test module.
-    $this->enableModules(array('config_test', 'config_schema_test'));
-    $this->installConfig(array('config_test', 'config_schema_test'));
-
-    // After module installation the new schema should exist.
-    $this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install exists.');
+    $this->enableModules(array('config_test'));
+    $this->installConfig(array('config_test'));
 
     // Verify that default module config exists.
     \Drupal::configFactory()->reset($default_config);
@@ -71,6 +68,13 @@ function testModuleInstallation() {
     $this->assertFalse(isset($GLOBALS['hook_config_test']['predelete']));
     $this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
 
+    // Install the schema test module.
+    $this->enableModules(array('config_schema_test'));
+    $this->installConfig(array('config_schema_test'));
+
+    // After module installation the new schema should exist.
+    $this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install exists.');
+
     // Ensure that data type casting is applied during config installation.
     $config = $this->config('config_schema_test.schema_in_install');
     $this->assertIdentical($config->get('integer'), 1);
@@ -171,7 +175,7 @@ public function testCollectionInstallationCollectionConfigEntity() {
     \Drupal::state()->set('config_collection_install_test.collection_names', $collections);
     // Install the test module.
     $this->enableModules(array('config_test', 'config_collection_install_test'));
-    $this->installConfig(array('config_test'));
+    $this->installConfig(array('config_test', 'config_collection_install_test'));
     /** @var \Drupal\Core\Config\StorageInterface $active_storage */
     $active_storage = \Drupal::service('config.storage');
     $this->assertEqual($collections, $active_storage->getAllCollectionNames());
diff --git a/core/modules/config/src/Tests/ConfigInstallWebTest.php b/core/modules/config/src/Tests/ConfigInstallWebTest.php
index b04ce39..5748d51 100644
--- a/core/modules/config/src/Tests/ConfigInstallWebTest.php
+++ b/core/modules/config/src/Tests/ConfigInstallWebTest.php
@@ -214,4 +214,21 @@ public function testPreExistingConfigInstall() {
       $this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.dotted.default, language/fr/config_test.dynamic.dotted.default) provided by config_clash_test_theme already exist in active configuration');
     }
   }
+
+  /**
+   * Tests unmet dependencies detection.
+   */
+  public function testUnmetDependenciesInstall() {
+    $this->drupalLogin($this->adminUser);
+    // We need to install separately since config_install_dependency_test does
+    // not depend on config_test and order is important.
+    $this->drupalPostForm('admin/modules', array('modules[Testing][config_test][enable]' => TRUE), t('Save configuration'));
+    $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_dependency_test][enable]' => TRUE), t('Save configuration'));
+    $this->assertRaw('Unable to install Config install dependency test, <em class="placeholder">config_test.dynamic.other_module_test_with_dependency</em> has unmet dependencies.');
+
+    $this->drupalPostForm('admin/modules', array('modules[Testing][config_other_module_config_test][enable]' => TRUE), t('Save configuration'));
+    $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_dependency_test][enable]' => TRUE), t('Save configuration'));
+    $this->rebuildContainer();
+    $this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
+  }
 }
diff --git a/core/modules/config/src/Tests/ConfigOtherModuleTest.php b/core/modules/config/src/Tests/ConfigOtherModuleTest.php
index 082e485..748d125 100644
--- a/core/modules/config/src/Tests/ConfigOtherModuleTest.php
+++ b/core/modules/config/src/Tests/ConfigOtherModuleTest.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Config\PreExistingConfigException;
 use Drupal\Core\Config\StorageInterface;
+use Drupal\Core\Config\UnmetDependenciesException;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -59,18 +60,10 @@ public function testInstallOtherModuleFirst() {
     // Default configuration provided by config_test should still exist.
     $this->assertTrue(entity_load('config_test', 'dotted.default', TRUE), 'The configuration is not deleted.');
 
-    // Re-enable module to test that pre-existing default configuration throws
-    // an error.
-    $msg = "The expected PreExistingConfigException is thrown by reinstalling config_other_module_config_test.";
-    try {
-      $this->installModule('config_other_module_config_test');
-      $this->fail($msg);
-    }
-    catch (PreExistingConfigException $e) {
-      $this->pass($msg);
-      $this->assertEqual($e->getExtension(), 'config_other_module_config_test');
-      $this->assertEqual($e->getConfigObjects(), [StorageInterface::DEFAULT_COLLECTION => ['config_test.dynamic.other_module_test']]);
-    }
+    // Re-enable module to test that pre-existing optional configuration does
+    // not throw an error.
+    $this->installModule('config_other_module_config_test');
+    $this->assertTrue(\Drupal::moduleHandler()->moduleExists('config_other_module_config_test'), 'The config_other_module_config_test module is installed.');
   }
 
   /**
@@ -97,6 +90,25 @@ public function testUninstall() {
   }
 
   /**
+   * Tests the configuration with unmet dependencies is not installed.
+   */
+  public function testDependencyChecking() {
+    try {
+      $this->installModule('config_install_dependency_test');
+      $this->fail('Expected UnmetDependenciesException not thrown.');
+    }
+    catch (UnmetDependenciesException $e) {
+      $this->assertEqual($e->getExtension(), 'config_install_dependency_test');
+      $this->assertEqual($e->getConfigObjects(), ['config_test.dynamic.other_module_test_with_dependency']);
+      $this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.other_module_test_with_dependency) provided by config_install_dependency_test have unmet dependencies');
+    }
+    $this->installModule('config_test');
+    $this->installModule('config_other_module_config_test');
+    $this->installModule('config_install_dependency_test');
+    $this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
+  }
+
+  /**
    * Installs a module.
    *
    * @param string $module
diff --git a/core/modules/config/tests/config_install_dependency_test/config/install/config_test.dynamic.other_module_test_with_dependency.yml b/core/modules/config/tests/config_install_dependency_test/config/install/config_test.dynamic.other_module_test_with_dependency.yml
new file mode 100644
index 0000000..69470f8
--- /dev/null
+++ b/core/modules/config/tests/config_install_dependency_test/config/install/config_test.dynamic.other_module_test_with_dependency.yml
@@ -0,0 +1,11 @@
+id: other_module_test_with_dependency
+label: 'Other module test with dependency'
+weight: 0
+style: ''
+status: true
+langcode: en
+protected_property: Default
+dependencies:
+  enforced:
+    module:
+      - config_other_module_config_test
diff --git a/core/modules/block/tests/modules/block_test/block_test.info.yml b/core/modules/config/tests/config_install_dependency_test/config_install_dependency_test.info.yml
similarity index 50%
copy from core/modules/block/tests/modules/block_test/block_test.info.yml
copy to core/modules/config/tests/config_install_dependency_test/config_install_dependency_test.info.yml
index 9a8895f..e9b3c72 100644
--- a/core/modules/block/tests/modules/block_test/block_test.info.yml
+++ b/core/modules/config/tests/config_install_dependency_test/config_install_dependency_test.info.yml
@@ -1,6 +1,5 @@
-name: 'Block test'
+name: 'Config install dependency test'
 type: module
-description: 'Provides test blocks.'
 package: Testing
 version: VERSION
 core: 8.x
diff --git a/core/modules/config/tests/config_other_module_config_test/config/install/config_test.dynamic.other_module_test.yml b/core/modules/config/tests/config_other_module_config_test/config/optional/config_test.dynamic.other_module_test.yml
similarity index 100%
rename from core/modules/config/tests/config_other_module_config_test/config/install/config_test.dynamic.other_module_test.yml
rename to core/modules/config/tests/config_other_module_config_test/config/optional/config_test.dynamic.other_module_test.yml
diff --git a/core/modules/contact/src/Tests/MessageEntityTest.php b/core/modules/contact/src/Tests/MessageEntityTest.php
index f888345..69ad3e8 100644
--- a/core/modules/contact/src/Tests/MessageEntityTest.php
+++ b/core/modules/contact/src/Tests/MessageEntityTest.php
@@ -32,7 +32,7 @@ class MessageEntityTest extends KernelTestBase {
 
   protected function setUp() {
     parent::setUp();
-    $this->installConfig(array('contact'));
+    $this->installConfig(array('contact', 'contact_test'));
   }
 
   /**
diff --git a/core/modules/editor/src/Tests/EditorFileUsageTest.php b/core/modules/editor/src/Tests/EditorFileUsageTest.php
index 3e66cef..7b5210e 100644
--- a/core/modules/editor/src/Tests/EditorFileUsageTest.php
+++ b/core/modules/editor/src/Tests/EditorFileUsageTest.php
@@ -28,6 +28,7 @@ protected function setUp() {
     $this->installEntitySchema('file');
     $this->installSchema('node', array('node_access'));
     $this->installSchema('file', array('file_usage'));
+    $this->installConfig(['node']);
 
     // Add text formats.
     $filtered_html_format = entity_create('filter_format', array(
diff --git a/core/modules/entity_reference/tests/modules/entity_reference_test/entity_reference_test.info.yml b/core/modules/entity_reference/tests/modules/entity_reference_test/entity_reference_test.info.yml
index 67a5f48..e465519 100644
--- a/core/modules/entity_reference/tests/modules/entity_reference_test/entity_reference_test.info.yml
+++ b/core/modules/entity_reference/tests/modules/entity_reference_test/entity_reference_test.info.yml
@@ -6,3 +6,6 @@ package: Testing
 version: VERSION
 dependencies:
   - entity_reference
+  - node
+  - user
+  - views
diff --git a/core/modules/field/src/Tests/FieldImportChangeTest.php b/core/modules/field/src/Tests/FieldImportChangeTest.php
index 8541664..c34ff31 100644
--- a/core/modules/field/src/Tests/FieldImportChangeTest.php
+++ b/core/modules/field/src/Tests/FieldImportChangeTest.php
@@ -31,6 +31,7 @@ class FieldImportChangeTest extends FieldUnitTestBase {
    * Tests importing an updated field.
    */
   function testImportChange() {
+    $this->installConfig(['field_test_config']);
     $field_storage_id = 'field_test_import';
     $field_id = "entity_test.entity_test.$field_storage_id";
     $field_config_name = "field.field.$field_id";
diff --git a/core/modules/field/src/Tests/FieldImportDeleteTest.php b/core/modules/field/src/Tests/FieldImportDeleteTest.php
index 030adfb..04eaecc 100644
--- a/core/modules/field/src/Tests/FieldImportDeleteTest.php
+++ b/core/modules/field/src/Tests/FieldImportDeleteTest.php
@@ -33,6 +33,7 @@ class FieldImportDeleteTest extends FieldUnitTestBase {
    * Tests deleting field storages and fields as part of config import.
    */
   public function testImportDelete() {
+    $this->installConfig(['field_test_config']);
     // At this point there are 5 field configuration objects in the active
     // storage.
     // - field.storage.entity_test.field_test_import
diff --git a/core/modules/field_ui/src/Tests/EntityDisplayTest.php b/core/modules/field_ui/src/Tests/EntityDisplayTest.php
index ef0f476..98d2e3c 100644
--- a/core/modules/field_ui/src/Tests/EntityDisplayTest.php
+++ b/core/modules/field_ui/src/Tests/EntityDisplayTest.php
@@ -27,7 +27,7 @@ class EntityDisplayTest extends KernelTestBase {
   protected function setUp() {
     parent::setUp();
     $this->installEntitySchema('node');
-    $this->installConfig(array('field'));
+    $this->installConfig(array('field', 'node'));
   }
 
   /**
diff --git a/core/modules/file/config/install/views.view.files.yml b/core/modules/file/config/optional/views.view.files.yml
similarity index 100%
rename from core/modules/file/config/install/views.view.files.yml
rename to core/modules/file/config/optional/views.view.files.yml
diff --git a/core/modules/filter/src/Tests/FilterAPITest.php b/core/modules/filter/src/Tests/FilterAPITest.php
index 50210b5..61c7766 100644
--- a/core/modules/filter/src/Tests/FilterAPITest.php
+++ b/core/modules/filter/src/Tests/FilterAPITest.php
@@ -28,7 +28,7 @@ class FilterAPITest extends EntityUnitTestBase {
   protected function setUp() {
     parent::setUp();
 
-    $this->installConfig(array('system', 'filter'));
+    $this->installConfig(array('system', 'filter', 'filter_test'));
   }
 
   /**
diff --git a/core/modules/filter/tests/filter_test/filter_test.info.yml b/core/modules/filter/tests/filter_test/filter_test.info.yml
index dcefe8e..6e377bc 100644
--- a/core/modules/filter/tests/filter_test/filter_test.info.yml
+++ b/core/modules/filter/tests/filter_test/filter_test.info.yml
@@ -4,3 +4,5 @@ description: 'Tests filter hooks and functions.'
 package: Testing
 version: VERSION
 core: 8.x
+dependencies:
+  - filter
diff --git a/core/modules/forum/config/install/rdf.mapping.node.forum.yml b/core/modules/forum/config/optional/rdf.mapping.node.forum.yml
similarity index 100%
rename from core/modules/forum/config/install/rdf.mapping.node.forum.yml
rename to core/modules/forum/config/optional/rdf.mapping.node.forum.yml
diff --git a/core/modules/forum/config/install/rdf.mapping.taxonomy_term.forums.yml b/core/modules/forum/config/optional/rdf.mapping.taxonomy_term.forums.yml
similarity index 100%
rename from core/modules/forum/config/install/rdf.mapping.taxonomy_term.forums.yml
rename to core/modules/forum/config/optional/rdf.mapping.taxonomy_term.forums.yml
diff --git a/core/modules/hal/src/Tests/EntityTest.php b/core/modules/hal/src/Tests/EntityTest.php
index a1b221d..9fb1014 100644
--- a/core/modules/hal/src/Tests/EntityTest.php
+++ b/core/modules/hal/src/Tests/EntityTest.php
@@ -35,6 +35,7 @@ protected function setUp() {
     $this->installSchema('system', array('sequences'));
     $this->installSchema('comment', array('comment_entity_statistics'));
     $this->installEntitySchema('taxonomy_term');
+    $this->installConfig(['node', 'comment']);
   }
 
   /**
diff --git a/core/modules/locale/config/install/tour.tour.locale.yml b/core/modules/locale/config/optional/tour.tour.locale.yml
similarity index 100%
rename from core/modules/locale/config/install/tour.tour.locale.yml
rename to core/modules/locale/config/optional/tour.tour.locale.yml
diff --git a/core/modules/node/config/install/search.page.node_search.yml b/core/modules/node/config/optional/search.page.node_search.yml
similarity index 100%
rename from core/modules/node/config/install/search.page.node_search.yml
rename to core/modules/node/config/optional/search.page.node_search.yml
diff --git a/core/modules/node/config/install/views.view.archive.yml b/core/modules/node/config/optional/views.view.archive.yml
similarity index 100%
rename from core/modules/node/config/install/views.view.archive.yml
rename to core/modules/node/config/optional/views.view.archive.yml
diff --git a/core/modules/node/config/install/views.view.content.yml b/core/modules/node/config/optional/views.view.content.yml
similarity index 100%
rename from core/modules/node/config/install/views.view.content.yml
rename to core/modules/node/config/optional/views.view.content.yml
diff --git a/core/modules/node/config/install/views.view.content_recent.yml b/core/modules/node/config/optional/views.view.content_recent.yml
similarity index 99%
rename from core/modules/node/config/install/views.view.content_recent.yml
rename to core/modules/node/config/optional/views.view.content_recent.yml
index bc12a13..4cb1dd7 100644
--- a/core/modules/node/config/install/views.view.content_recent.yml
+++ b/core/modules/node/config/optional/views.view.content_recent.yml
@@ -423,3 +423,5 @@ display:
     id: block_1
     display_title: Block
     position: 1
+    display_options:
+      display_extenders: {  }
diff --git a/core/modules/node/config/install/views.view.frontpage.yml b/core/modules/node/config/optional/views.view.frontpage.yml
similarity index 100%
rename from core/modules/node/config/install/views.view.frontpage.yml
rename to core/modules/node/config/optional/views.view.frontpage.yml
diff --git a/core/modules/node/config/install/views.view.glossary.yml b/core/modules/node/config/optional/views.view.glossary.yml
similarity index 100%
rename from core/modules/node/config/install/views.view.glossary.yml
rename to core/modules/node/config/optional/views.view.glossary.yml
diff --git a/core/modules/node/src/Tests/NodeBodyFieldStorageTest.php b/core/modules/node/src/Tests/NodeBodyFieldStorageTest.php
index bdfa24c..54f83f2 100644
--- a/core/modules/node/src/Tests/NodeBodyFieldStorageTest.php
+++ b/core/modules/node/src/Tests/NodeBodyFieldStorageTest.php
@@ -36,7 +36,7 @@ protected function setUp() {
     $this->installSchema('user', 'users_data');
     $this->installEntitySchema('user');
     $this->installEntitySchema('node');
-    $this->installConfig(array('field'));
+    $this->installConfig(array('field', 'node'));
   }
 
   /**
diff --git a/core/modules/node/src/Tests/NodeTokenReplaceTest.php b/core/modules/node/src/Tests/NodeTokenReplaceTest.php
index 7c0dfe7..7431f06 100644
--- a/core/modules/node/src/Tests/NodeTokenReplaceTest.php
+++ b/core/modules/node/src/Tests/NodeTokenReplaceTest.php
@@ -30,7 +30,7 @@ class NodeTokenReplaceTest extends TokenReplaceUnitTestBase {
    */
   protected function setUp() {
     parent::setUp();
-    $this->installConfig(array('filter'));
+    $this->installConfig(array('filter', 'node'));
 
     $node_type = entity_create('node_type', array('type' => 'article', 'name' => 'Article'));
     $node_type->save();
diff --git a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
index 4d09490..4a0cbc4 100644
--- a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
+++ b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
@@ -191,6 +191,8 @@ function testInstallEntitySchema() {
    * Tests expected behavior of installConfig().
    */
   function testInstallConfig() {
+    // The user module has configuration that depends on system.
+    $this->enableModules(array('system'));
     $module = 'user';
 
     // Verify that default config can only be installed for enabled modules.
diff --git a/core/modules/system/src/Controller/ThemeController.php b/core/modules/system/src/Controller/ThemeController.php
index 7126a7e..d82f908 100644
--- a/core/modules/system/src/Controller/ThemeController.php
+++ b/core/modules/system/src/Controller/ThemeController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Config\PreExistingConfigException;
+use Drupal\Core\Config\UnmetDependenciesException;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Extension\ThemeHandlerInterface;
 use Drupal\Core\Routing\RouteBuilderIndicatorInterface;
@@ -144,6 +145,9 @@ public function install(Request $request) {
           'error'
         );
       }
+      catch (UnmetDependenciesException $e) {
+        drupal_set_message($e->getTranslatedMessage($this->getStringTranslation(), $theme), 'error');
+      }
 
       return $this->redirect('system.themes_page');
     }
diff --git a/core/modules/system/src/Form/ModulesListConfirmForm.php b/core/modules/system/src/Form/ModulesListConfirmForm.php
index b3c7977..5449b41 100644
--- a/core/modules/system/src/Form/ModulesListConfirmForm.php
+++ b/core/modules/system/src/Form/ModulesListConfirmForm.php
@@ -8,6 +8,7 @@
 namespace Drupal\system\Form;
 
 use Drupal\Core\Config\PreExistingConfigException;
+use Drupal\Core\Config\UnmetDependenciesException;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Extension\ModuleInstallerInterface;
 use Drupal\Core\Form\ConfirmFormBase;
@@ -176,6 +177,13 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
         );
         return;
       }
+      catch (UnmetDependenciesException $e) {
+        drupal_set_message(
+          $e->getTranslatedMessage($this->getStringTranslation(), $this->modules['install'][$e->getExtension()]),
+          'error'
+        );
+        return;
+      }
     }
 
     // Gets module list after install process, flushes caches and displays a
diff --git a/core/modules/system/src/Form/ModulesListForm.php b/core/modules/system/src/Form/ModulesListForm.php
index 2e80482..90a6e9f 100644
--- a/core/modules/system/src/Form/ModulesListForm.php
+++ b/core/modules/system/src/Form/ModulesListForm.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Config\PreExistingConfigException;
+use Drupal\Core\Config\UnmetDependenciesException;
 use Drupal\Core\Controller\TitleResolverInterface;
 use Drupal\Core\Access\AccessManagerInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
@@ -536,6 +537,13 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
         );
         return;
       }
+      catch (UnmetDependenciesException $e) {
+        drupal_set_message(
+          $e->getTranslatedMessage($this->getStringTranslation(), $modules['install'][$e->getExtension()]),
+          'error'
+        );
+        return;
+      }
     }
 
     // Gets module list after install process, flushes caches and displays a
diff --git a/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php b/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php
index 9386983..deff706 100644
--- a/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php
@@ -52,6 +52,7 @@ protected function setUp() {
     $this->installSchema('file', array('file_usage'));
     $this->installSchema('node', array('node_access'));
     $this->installSchema('comment', array('comment_entity_statistics'));
+    $this->installConfig(['node', 'comment']);
   }
 
   /**
diff --git a/core/modules/system/tests/themes/test_basetheme/config/install/core.date_format.fancy.yml b/core/modules/system/tests/themes/test_basetheme/config/install/core.date_format.fancy.yml
index 37a0bf8..05d5e27 100644
--- a/core/modules/system/tests/themes/test_basetheme/config/install/core.date_format.fancy.yml
+++ b/core/modules/system/tests/themes/test_basetheme/config/install/core.date_format.fancy.yml
@@ -7,3 +7,7 @@ status: true
 langcode: en
 locked: false
 pattern: 'U'
+dependencies:
+  enforced:
+    theme:
+      - test_basetheme
diff --git a/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml b/core/modules/taxonomy/config/optional/views.view.taxonomy_term.yml
similarity index 100%
rename from core/modules/taxonomy/config/install/views.view.taxonomy_term.yml
rename to core/modules/taxonomy/config/optional/views.view.taxonomy_term.yml
diff --git a/core/modules/user/config/install/rdf.mapping.user.user.yml b/core/modules/user/config/optional/rdf.mapping.user.user.yml
similarity index 100%
rename from core/modules/user/config/install/rdf.mapping.user.user.yml
rename to core/modules/user/config/optional/rdf.mapping.user.user.yml
diff --git a/core/modules/user/config/install/search.page.user_search.yml b/core/modules/user/config/optional/search.page.user_search.yml
similarity index 100%
rename from core/modules/user/config/install/search.page.user_search.yml
rename to core/modules/user/config/optional/search.page.user_search.yml
diff --git a/core/modules/user/config/install/views.view.user_admin_people.yml b/core/modules/user/config/optional/views.view.user_admin_people.yml
similarity index 100%
rename from core/modules/user/config/install/views.view.user_admin_people.yml
rename to core/modules/user/config/optional/views.view.user_admin_people.yml
diff --git a/core/modules/user/config/install/views.view.who_s_new.yml b/core/modules/user/config/optional/views.view.who_s_new.yml
similarity index 100%
rename from core/modules/user/config/install/views.view.who_s_new.yml
rename to core/modules/user/config/optional/views.view.who_s_new.yml
diff --git a/core/modules/user/config/install/views.view.who_s_online.yml b/core/modules/user/config/optional/views.view.who_s_online.yml
similarity index 100%
rename from core/modules/user/config/install/views.view.who_s_online.yml
rename to core/modules/user/config/optional/views.view.who_s_online.yml
diff --git a/core/modules/user/user.info.yml b/core/modules/user/user.info.yml
index 45a421a..8aec70e 100644
--- a/core/modules/user/user.info.yml
+++ b/core/modules/user/user.info.yml
@@ -6,3 +6,5 @@ version: VERSION
 core: 8.x
 required: true
 configure: user.admin_index
+dependencies:
+  - system
diff --git a/core/modules/views/src/Tests/ModuleTest.php b/core/modules/views/src/Tests/ModuleTest.php
index 1f52a19..34f3dd5 100644
--- a/core/modules/views/src/Tests/ModuleTest.php
+++ b/core/modules/views/src/Tests/ModuleTest.php
@@ -142,7 +142,7 @@ public function customErrorHandler($error_level, $message, $filename, $line, $co
    * Tests the load wrapper/helper functions.
    */
   public function testLoadFunctions() {
-    $this->enableModules(array('node'));
+    $this->enableModules(array('field', 'text', 'node'));
     $this->installConfig(array('node'));
     $storage = $this->container->get('entity.manager')->getStorage('view');
 
diff --git a/core/modules/views/src/Tests/ViewExecutableTest.php b/core/modules/views/src/Tests/ViewExecutableTest.php
index 27c4e55..6affac2 100644
--- a/core/modules/views/src/Tests/ViewExecutableTest.php
+++ b/core/modules/views/src/Tests/ViewExecutableTest.php
@@ -20,6 +20,7 @@
 use Drupal\views\Plugin\views\query\Sql;
 use Drupal\views\Plugin\views\pager\PagerPluginBase;
 use Drupal\views\Plugin\views\query\QueryPluginBase;
+use Drupal\views\ViewsDataHelper;
 use Drupal\views_test_data\Plugin\views\display\DisplayTest;
 use Symfony\Component\HttpFoundation\Response;
 
@@ -83,7 +84,9 @@ protected function setUpFixtures() {
     $this->installEntitySchema('node');
     $this->installEntitySchema('comment');
     $this->installSchema('comment', array('comment_entity_statistics'));
-    $this->installConfig(array('field'));
+    $this->installConfig(array('system', 'field', 'node', 'comment'));
+    // Clear the views data cache so it updated with the new entity information.
+    \Drupal::service('views.views_data')->clear();
 
     entity_create('node_type', array(
       'type' => 'page',
diff --git a/core/modules/views_ui/config/install/tour.tour.views-ui.yml b/core/modules/views_ui/config/optional/tour.tour.views-ui.yml
similarity index 100%
rename from core/modules/views_ui/config/install/tour.tour.views-ui.yml
rename to core/modules/views_ui/config/optional/tour.tour.views-ui.yml
diff --git a/core/profiles/minimal/minimal.info.yml b/core/profiles/minimal/minimal.info.yml
index 628dcd3..d1f83de 100644
--- a/core/profiles/minimal/minimal.info.yml
+++ b/core/profiles/minimal/minimal.info.yml
@@ -7,3 +7,5 @@ dependencies:
   - node
   - block
   - dblog
+themes:
+  - stark
diff --git a/core/profiles/testing/config/install/locale.settings.yml b/core/profiles/testing/config/optional/locale.settings.yml
similarity index 100%
rename from core/profiles/testing/config/install/locale.settings.yml
rename to core/profiles/testing/config/optional/locale.settings.yml
