diff --git a/core/includes/update.inc b/core/includes/update.inc
index e1f35be..a294490 100644
--- a/core/includes/update.inc
+++ b/core/includes/update.inc
@@ -15,6 +15,7 @@
 use Drupal\Core\DrupalKernel;
 use Drupal\Component\Uuid\Uuid;
 use Drupal\Component\Utility\NestedArray;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Minimum schema version of Drupal 7 required for upgrade to Drupal 8.
@@ -102,8 +103,10 @@ function update_prepare_d8_bootstrap() {
   $settings['cache']['default'] = 'cache.backend.memory';
   new Settings($settings);
 
-  // Enable UpdateBundle service overrides.
-  $GLOBALS['conf']['container_bundles'][] = 'Drupal\Core\DependencyInjection\UpdateBundle';
+  // Enable UpdateBundle service overrides. While the container_bundles array
+  // does not need a key, let's use so it can be removed once the upgrade are
+  // finished. @see update_flush_all_caches()
+  $GLOBALS['conf']['container_bundles']['UpdateBundle'] = 'Drupal\Core\DependencyInjection\UpdateBundle';
 
   // Check whether settings.php needs to be rewritten.
   $settings_exist = !empty($GLOBALS['config_directories']);
@@ -351,10 +354,6 @@ function update_prepare_d8_bootstrap() {
         ':schema_uninstalled' => SCHEMA_UNINSTALLED,
       ));
 
-      // Populate a fixed module list (again, why did it get lost?) to avoid
-      // errors due to the drupal_alter() in _system_rebuild_module_data().
-      $module_list['system'] = 'core/modules/system/system.module';
-      drupal_container()->get('module_handler')->setModuleList($module_list);
       $module_data = _system_rebuild_module_data();
 
       // Migrate each extension into configuration, varying by the extension's
@@ -452,6 +451,9 @@ function update_prepare_stored_includes() {
  */
 function update_prepare_d8_language() {
   if (db_table_exists('languages')) {
+
+    module_enable(array('language'));
+
     $languages = db_select('languages', 'l')
       ->fields('l')
       ->execute();
@@ -490,12 +492,6 @@ function update_prepare_d8_language() {
     // Rename the languages table to language.
     db_rename_table('languages', 'language');
 
-    // Install/enable the language module. We need to use the update specific
-    // version of this function to ensure schema conflicts don't happen due to
-    // our updated data.
-    $modules = array('language');
-    update_module_enable($modules);
-
     // Rename language column to langcode and set it again as the primary key.
     if (db_field_exists('language', 'language')) {
       db_drop_primary_key('language');
@@ -631,7 +627,7 @@ function update_fix_d8_requirements() {
 
     // Make sure that file.module is enabled as it is required for the user
     // picture upgrade path.
-    update_module_enable(array('file'));
+    module_enable(array('file'));
 
     $schema = array(
       'description' => 'Generic key/value storage table with an expiration.',
@@ -671,73 +667,33 @@ function update_fix_d8_requirements() {
     );
     db_create_table('key_value_expire', $schema);
 
+    // Views module is required to convert all previously existing listings into
+    // views configurations.
+    // Like any other module APIs and services, Views' services are not available
+    // in update.php. Existing listings are migrated into configuration, using
+    // the limited standard tools of raw database queries and config().
+    module_enable(array('views'));
+
     update_variable_set('update_d8_requirements', TRUE);
   }
 }
 
 /**
- * Installs a new module in Drupal 8 via hook_update_N().
+ * Forces a module to a given schema version.
  *
- * @param array $modules
- *   List of modules to enable. Dependencies are not checked and must be
- *   ensured by the caller.
- * @param bool $update_schema_version
- *   (optional) The schema version the module should be set to. Defaults to 0
- *   which works well for completely new modules.
+ * This function is rarely necessary.
  *
- * @return array
- *   List of the old schema versions keyed by the module names,
- *   SCHEMA_UNINSTALLED if it was not installed before. Additional manual
- *   installation steps like installing default configuration might be necessary
- *   if the module was not installed before.
+ * @param string $module
+ *   Name of the module.
+ * @param string $schema_version
+ *   The schema version the module should be set to.
  */
-function update_module_enable(array $modules, $schema_version = 0) {
-  $schema_store = Drupal::keyValue('system.schema');
-  $old_schema = array();
-  foreach ($modules as $module) {
-    // Check for initial schema and install it. The schema version of a newly
-    // installed module is always 0. Using 8000 here would be inconsistent
-    // since $module_update_8000() may involve a schema change, and we want
-    // to install the schema as it was before any updates were added.
-    module_load_install($module);
-    $function = $module . '_schema_0';
-    if (function_exists($function)) {
-      $schema = $function();
-      foreach ($schema as $table => $spec) {
-        db_create_table($table, $spec);
-      }
-    }
-    // Enable the module with a weight of 0.
-    $module_config = config('system.module');
-    $module_config
-      ->set("enabled.$module", 0)
-      ->set('enabled', module_config_sort($module_config->get('enabled')))
-      ->save();
-    // Ensure the module is not contained in disabled modules.
-    config('system.module.disabled')
-      ->clear($module)
-      ->save();
-
-    $current_schema = $schema_store->get($module);
-    // Set the schema version if the module was not just disabled before.
-    if ($current_schema === NULL || $current_schema === SCHEMA_UNINSTALLED) {
-      // Change the schema version to the given value (defaults to 0), so any
-      // module updates since the module's inception are executed in a core
-      // upgrade.
-      $schema_store->set($module, $schema_version);
-      $old_schema[$module] = SCHEMA_UNINSTALLED;
-    }
-    else {
-      $old_schema[$module] = $current_schema;
-    }
-
-    // system_list_reset() is in module.inc but that would only be available
-    // once the variable bootstrap is done.
-    require_once __DIR__ . '/module.inc';
-    system_list_reset();
-    //  @todo: figure out what to do about hook_install() and hook_enable().
-  }
-  return $old_schema;
+function update_set_schema($module, $schema_version) {
+  Drupal::keyValue('system.schema')->set($module, $schema_version);
+  // system_list_reset() is in module.inc but that would only be available
+  // once the variable bootstrap is done.
+  require_once __DIR__ . '/module.inc';
+  system_list_reset();
 }
 
 /**
@@ -935,7 +891,7 @@ function update_batch($start, $redirect = NULL, $url = NULL, $batch = array(), $
  */
 function update_finished($success, $results, $operations) {
   // Clear the caches in case the data has been updated.
-  drupal_flush_all_caches();
+  update_flush_all_caches();
 
   $_SESSION['update_results'] = $results;
   $_SESSION['update_success'] = $success;
@@ -1271,7 +1227,7 @@ function update_retrieve_dependencies() {
       // Nothing to upgrade.
       continue;
     }
-     $function = $module . '_update_dependencies';
+    $function = $module . '_update_dependencies';
     // Ensure install file is loaded.
     module_load_install($module);
     if (function_exists($function)) {
diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
index 913a43b..a45d338 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
@@ -218,7 +218,7 @@ public function deleteMultiple(array $cids) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags().
    */
   public function deleteTags(array $tags) {
-    $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache');
+    $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
     foreach ($this->flattenTags($tags) as $tag) {
       unset($tag_cache[$tag]);
       try {
@@ -277,7 +277,7 @@ public function invalidateMultiple(array $cids) {
    */
   public function invalidateTags(array $tags) {
     try {
-      $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache');
+      $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
       foreach ($this->flattenTags($tags) as $tag) {
         unset($tag_cache[$tag]);
         $this->connection->merge('cache_tags')
diff --git a/core/lib/Drupal/Core/DependencyInjection/UpdateBundle.php b/core/lib/Drupal/Core/DependencyInjection/UpdateBundle.php
index 06a9522..91528e5 100644
--- a/core/lib/Drupal/Core/DependencyInjection/UpdateBundle.php
+++ b/core/lib/Drupal/Core/DependencyInjection/UpdateBundle.php
@@ -32,6 +32,12 @@ public function build(SymfonyContainerBuilder $container) {
     $container
       ->register('config.storage', 'Drupal\Core\Config\FileStorage')
       ->addArgument(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY));
+    $container->register('module_handler', 'Drupal\Core\Extension\UpdateModuleHandler')
+      ->addArgument('%container.modules%');
+    $container
+      ->register("cache_factory", 'Drupal\Core\Cache\MemoryBackendFactory');
+    $container
+      ->register('router.builder', 'Drupal\Core\Routing\RouteBuilderStatic');
   }
 
 }
diff --git a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php
new file mode 100644
index 0000000..074ba93
--- /dev/null
+++ b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php
@@ -0,0 +1,146 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Extension\UpdateModuleHandler.
+ */
+
+namespace Drupal\Core\Extension;
+
+use Drupal\Core\Config\ConfigException;
+use Drupal\Core\Config\FileStorage;
+
+/**
+ * Deals with module enables and throws exception if hooks fired during updates.
+ *
+ * This is necessary for a reliable and testable update environment.
+ */
+class UpdateModuleHandler extends ModuleHandler {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getImplementations($hook) {
+    if (substr($hook, -6) === '_alter') {
+      return array();
+    }
+    switch ($hook) {
+      // hook_requirements is necessary for updates to work.
+      case 'requirements':
+      // Allow logging.
+      case 'watchdog':
+        return parent::getImplementations($hook);
+      // Forms and pages do not render without the basic elements defined in
+      // system_element_info().
+      case 'element_info':
+      // Forms do not render without the basic elements in
+      // drupal_common_theme() called from system_theme().
+      case 'theme':
+      // user_update_8011() uses public:// to create the image style directory.
+      case 'stream_wrappers':
+        return array('system');
+      // This is called during rebuild to find testing themes.
+      case 'system_theme_info':
+      // @todo: remove this.
+      case 'entity_info':
+        return array();
+      // t() in system_stream_wrappers() needs this. Other schema calls aren't
+      // supported.
+      case 'schema':
+        return array('locale');
+      default:
+        throw new \LogicException("Invoking hooks $hook is not supported during updates");
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function enable($module_list, $enable_dependencies = TRUE) {
+    $schema_store = \Drupal::keyValue('system.schema');
+    $old_schema = array();
+    foreach ($module_list as $module) {
+      // Check for initial schema and install it. The schema version of a newly
+      // installed module is always 0. Using 8000 here would be inconsistent
+      // since $module_update_8000() may involve a schema change, and we want
+      // to install the schema as it was before any updates were added.
+      module_load_install($module);
+      $function = $module . '_schema_0';
+      if (function_exists($function)) {
+        $schema = $function();
+        foreach ($schema as $table => $spec) {
+          db_create_table($table, $spec);
+        }
+      }
+      // Enable the module with a weight of 0.
+      $module_config = config('system.module');
+      $module_config
+        ->set("enabled.$module", 0)
+        ->set('enabled', module_config_sort($module_config->get('enabled')))
+        ->save();
+      // Ensure the module is not contained in disabled modules.
+      config('system.module.disabled')
+        ->clear($module)
+        ->save();
+
+      $current_schema = $schema_store->get($module);
+      // Set the schema version if the module was not just disabled before.
+      if ($current_schema === NULL || $current_schema === SCHEMA_UNINSTALLED) {
+        // Change the schema version to the given value (defaults to 0), so any
+        // module updates since the module's inception are executed in a core
+        // upgrade.
+        $schema_store->set($module, 0);
+        $old_schema[$module] = SCHEMA_UNINSTALLED;
+      }
+      else {
+        $old_schema[$module] = $current_schema;
+      }
+
+      // Copy the default configuration of the module into the active storage.
+      // The default configuration is not altered in any way, and since the module
+      // is just being installed, none of its configuration can exist already, so
+      // this is a plain copy operation from one storage to another.
+      $module_config_path = drupal_get_path('module', $module) . '/config';
+      if (is_dir($module_config_path)) {
+        $module_filestorage = new FileStorage($module_config_path);
+        $config_storage = drupal_container()->get('config.storage');
+        foreach ($module_filestorage->listAll() as $config_name) {
+          // If this file already exists, something in the upgrade path went
+          // completely wrong and we want to know.
+          if ($config_storage->exists($config_name)) {
+            throw new ConfigException(format_string('Default configuration file @name of @module module unexpectedly exists already before the module was installed.', array(
+              '@module' => $module,
+              '@name' => $config_name,
+            )));
+          }
+          $config_storage->write($config_name, $module_filestorage->read($config_name));
+        }
+      }
+
+      // system_list_reset() is in module.inc but that would only be available
+      // once the variable bootstrap is done.
+      require_once DRUPAL_ROOT . '/core/includes/module.inc';
+      system_list_reset();
+      $this->moduleList[$module] = drupal_get_filename('module', $module);
+      $this->load($module);
+      drupal_classloader_register($module, dirname($this->moduleList[$module]));
+      // @todo Figure out what to do about hook_install() and hook_enable().
+    }
+    return $old_schema;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function disable($module_list, $disable_dependents = TRUE) {
+    throw new \LogicException('Disabling modules is not supported during updates');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function uninstall($module_list = array(), $uninstall_dependents = TRUE) {
+    throw new \LogicException('Uninstalling modules is not supported during updates');
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/RouteBuilderStatic.php b/core/lib/Drupal/Core/Routing/RouteBuilderStatic.php
new file mode 100644
index 0000000..d6161f6
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RouteBuilderStatic.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Routing\RouteBuilderStatic.
+ */
+
+namespace Drupal\Core\Routing;
+
+/**
+ * This builds a static version of the router.
+ */
+class RouteBuilderStatic {
+
+  /**
+   * Rebuilds router.
+   */
+  public function rebuild() {
+    // @todo Add the route for the batch pages when that conversion happens,
+    //   http://drupal.org/node/1987816.
+  }
+
+}
diff --git a/core/modules/block/block.install b/core/modules/block/block.install
index 8e0d224..8379d18 100644
--- a/core/modules/block/block.install
+++ b/core/modules/block/block.install
@@ -180,7 +180,7 @@ function block_update_8005() {
  * Enable the Custom Block module.
  */
 function block_update_8006() {
-  update_module_enable(array('custom_block'));
+  module_enable(array('custom_block'));
 }
 
 /**
diff --git a/core/modules/language/language.install b/core/modules/language/language.install
index b16c53b..17828fd 100644
--- a/core/modules/language/language.install
+++ b/core/modules/language/language.install
@@ -117,3 +117,13 @@ function language_disable() {
   // will be FALSE, because the language module is disabled.
   variable_set('language_count', 1);
 }
+
+/**
+ * Implements hook_requirements().
+ */
+function language_requirements($phase) {
+  if ($phase == 'update') {
+    // Load the include files to make constants available for updates.
+    language_negotiation_include();
+  }
+}
diff --git a/core/modules/node/node.install b/core/modules/node/node.install
index a4cc155..080ff20 100644
--- a/core/modules/node/node.install
+++ b/core/modules/node/node.install
@@ -789,7 +789,7 @@ function node_update_8011() {
  */
 function node_update_8012() {
   // Enable the history module without re-installing the schema.
-  update_module_enable(array('history'));
+  module_enable(array('history'));
 }
 
 /**
@@ -812,7 +812,7 @@ function node_update_8013() {
  */
 function node_update_8014() {
   // Enable the datetime module.
-  update_module_enable(array('datetime'));
+  module_enable(array('datetime'));
 }
 
 /**
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 3824136..c8dcbfd 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1,13 +1,13 @@
 <?php
 
-use Drupal\Component\Utility\Crypt;
-use Drupal\Core\Database\Database;
-use Drupal\Core\Language\Language;
-
 /**
  * @file
  * Install, update and uninstall functions for the system module.
  */
+use Drupal\Component\Utility\Crypt;
+use Drupal\Core\Config\FileStorage;
+use Drupal\Core\Database\Database;
+use Drupal\Core\Language\Language;
 
 /**
  * Test and report Drupal installation requirements.
@@ -1539,7 +1539,7 @@ function system_update_8020() {
       ->condition('aid', 'system_block_ip_action')
       ->execute();
     // Enable the new Ban module.
-    update_module_enable(array('ban'));
+    module_enable(array('ban'));
   }
   else {
     // Drop old table.
@@ -1552,7 +1552,7 @@ function system_update_8020() {
  */
 function system_update_8021() {
   // Enable the module without re-installing the schema.
-  update_module_enable(array('action'));
+  module_enable(array('action'));
   // Rename former System module actions.
   $map = array(
     'system_message_action' => 'action_message_action',
@@ -1857,7 +1857,7 @@ function system_update_8041() {
  * Enable the new Entity module.
  */
 function system_update_8042() {
-  update_module_enable(array('entity'));
+  module_enable(array('entity'));
 }
 
 /**
@@ -1922,18 +1922,21 @@ function system_update_8045() {
 function system_update_8046() {
   $front_page = config('system.site')->get('page.front');
   if (!isset($front_page) || $front_page == 'node') {
-    update_module_enable(array('views'));
-
-    // Register views to the container, so views can use it's services.
-    $module_list = drupal_container()->getParameter('container.modules');
-    drupal_load('module', 'views');
-
-    drupal_container()->get('kernel')->updateModules($module_list, array('views' => 'core/modules/views/views.module'));
-
-    // This does not fire a hook just calls views.
-    config_install_default_config('module', 'views');
     // This imports the node frontpage view.
-    config_install_default_config('module', 'node');
+    $module = 'node';
+    $config_name = 'views.view.frontpage';
+    $module_config_path = drupal_get_path('module', $module) . '/config';
+    $module_filestorage = new FileStorage($module_config_path);
+    $config_storage = drupal_container()->get('config.storage');
+    // If this file already exists, something in the upgrade path went
+    // completely wrong and we want to know.
+    if ($config_storage->exists($config_name)) {
+      throw new ConfigException(format_string('Default configuration file @name of @module module unexpectedly exists already before the module was installed.', array(
+        '@module' => $module,
+        '@name' => $config_name,
+      )));
+    }
+    $config_storage->write($config_name, $module_filestorage->read($config_name));
   }
 }
 
@@ -1960,7 +1963,7 @@ function system_update_8047() {
  */
 function system_update_8048() {
   // Enable the module without re-installing the schema.
-  update_module_enable(array('menu_link'));
+  module_enable(array('menu_link'));
 
   // Add the langcode column if it doesn't exist.
   if (!db_field_exists('menu_inks', 'langcode')) {
diff --git a/core/modules/toolbar/toolbar.install b/core/modules/toolbar/toolbar.install
index 6732bb9..3d4f517 100644
--- a/core/modules/toolbar/toolbar.install
+++ b/core/modules/toolbar/toolbar.install
@@ -18,7 +18,7 @@
  */
 function toolbar_update_8000() {
   // Enable the modules without re-installing the schema.
-  update_module_enable(array('breakpoint'));
+  module_enable(array('breakpoint'));
 }
 
 /**
diff --git a/core/modules/user/user.install b/core/modules/user/user.install
index 9819cde..683957a 100644
--- a/core/modules/user/user.install
+++ b/core/modules/user/user.install
@@ -657,10 +657,11 @@ function user_update_8011() {
   // User pictures can only be migrated to the new user picture image field
   // if Image module is installed.
   if (!module_exists('image')) {
-    // Install image.module with schema version 8002 as a previous version
-    // would have to create tables that would be removed again.
-    $old_schema = update_module_enable(array('image'), 8002);
+    $old_schema = module_enable(array('image'));
     if ($old_schema['image'] == SCHEMA_UNINSTALLED) {
+      // Install image.module with schema version 8002 as a previous version
+      // would have to create tables that would be removed again.
+      update_set_schema('image', 8002);
       // If image.module was not installed before, install default
       // configuration and run the install hook.
       config_install_default_config('module', 'image');
diff --git a/core/modules/views/views.install b/core/modules/views/views.install
index aea8d0c..d3c0dee 100644
--- a/core/modules/views/views.install
+++ b/core/modules/views/views.install
@@ -17,7 +17,7 @@ function views_install() {
 /**
  * Provide an initial schema.
  *
- * @see update_module_enable().
+ * @see UpdateModuleHandler::enable().
  */
 function views_schema_0() {
   module_load_install('system');
diff --git a/core/update.php b/core/update.php
index cee23c4..deb3f52 100644
--- a/core/update.php
+++ b/core/update.php
@@ -14,6 +14,7 @@
  * back to its original state!
  */
 
+use Drupal\Core\DrupalKernel;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\DependencyInjection\Reference;
@@ -134,7 +135,7 @@ function update_script_selection_form($form, &$form_state) {
     );
 
     // No updates to run, so caches won't get flushed later.  Clear them now.
-    drupal_flush_all_caches();
+    update_flush_all_caches();
   }
   else {
     $form['help'] = array(
@@ -179,6 +180,20 @@ function update_helpful_links() {
 }
 
 /**
+ * Remove update overrides and flush all caches.
+ *
+ * This will need to be run once all (if any) updates are run. Do not call this
+ * while updates are running.
+ */
+function update_flush_all_caches() {
+  unset($GLOBALS['conf']['container_bundles']['UpdateBundle']);
+  Drupal::service('kernel')->updateModules(Drupal::moduleHandler()->getModuleList());
+
+  // No updates to run, so caches won't get flushed later.  Clear them now.
+  drupal_flush_all_caches();
+}
+
+/**
  * Displays results of the update script with any accompanying errors.
  */
 function update_results_page() {
@@ -435,13 +450,6 @@ function update_check_requirements($skip_warnings = FALSE) {
   require_once __DIR__ . '/includes/install.inc';
   require_once DRUPAL_ROOT . '/core/modules/system/system.install';
 
-  // Load module basics.
-  include_once __DIR__ . '/includes/module.inc';
-  $module_list['system'] = 'core/modules/system/system.module';
-  $module_handler = drupal_container()->get('module_handler');
-  $module_handler->setModuleList($module_list);
-  $module_handler->load('system');
-
   // Set up $language, since the installer components require it.
   drupal_language_initialize();
 
