diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index c3f15db29b0b51267dd0e4e2f403140dabaa011e..0da2d4066508f09059da8d5b49c9d8af24fc04ae 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -183,6 +183,8 @@ function install_state_defaults() {
     'config_verified' => FALSE,
     // TRUE when there is a valid database connection.
     'database_verified' => FALSE,
+    // TRUE if database is empty & ready to install.
+    'database_ready' => FALSE,
     // TRUE when a valid settings.php exists (containing both database
     // connection information and config directory names).
     'settings_verified' => FALSE,
@@ -335,9 +337,6 @@ function install_begin_request($class_loader, &$install_state) {
   $container
     ->register('string_translation', 'Drupal\Core\StringTranslation\TranslationManager')
     ->addArgument(new Reference('language_manager'));
-  $container
-    ->register('path.matcher', 'Drupal\Core\Path\PathMatcher')
-    ->addArgument(new Reference('config.factory'));
 
   // Register the stream wrapper manager.
   $container
@@ -352,6 +351,13 @@ function install_begin_request($class_loader, &$install_state) {
   $install_state['database_verified'] = install_verify_database_settings();
   $install_state['settings_verified'] = $install_state['config_verified'] && $install_state['database_verified'];
 
+  // Install factory tables only after checking the database.
+  if ($install_state['database_verified'] && $install_state['database_ready']) {
+    $container
+      ->register('path.matcher', 'Drupal\Core\Path\PathMatcher')
+      ->addArgument(new Reference('config.factory'));
+  }
+
   if ($install_state['settings_verified']) {
     try {
       $system_schema = system_schema();
@@ -704,6 +710,9 @@ function install_tasks($install_state) {
       'run' => $install_state['settings_verified'] ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_NOT_COMPLETED,
       'function' => 'Drupal\Core\Installer\Form\SiteSettingsForm',
     ),
+    'install_verify_database_ready' => array(
+      'run' => $install_state['database_ready'] ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_NOT_COMPLETED,
+    ),
     'install_base_system' => array(
       'run' => $install_state['base_system_verified'] ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_NOT_COMPLETED,
     ),
@@ -1050,6 +1059,21 @@ function install_verify_database_settings() {
   return FALSE;
 }
 
+/**
+ * Verify that the database is ready (no existing Drupal installation).
+ */
+function install_verify_database_ready() {
+  $system_schema = system_schema();
+  end($system_schema);
+  $table = key($system_schema);
+
+  if ($database = Database::getConnectionInfo()) {
+    if (Database::getConnection()->schema()->tableExists($table)) {
+      throw new AlreadyInstalledException(\Drupal::service('string_translation'));
+    }
+  }
+}
+
 /**
  * Checks a database connection and returns any errors.
  */
diff --git a/core/modules/system/src/Tests/Installer/InstallerExistingInstallationTest.php b/core/modules/system/src/Tests/Installer/InstallerExistingInstallationTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..08198db99059a348a1a974b4607743360009f7f3
--- /dev/null
+++ b/core/modules/system/src/Tests/Installer/InstallerExistingInstallationTest.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Installer\InstallerExistingInstallationTest.
+ */
+
+namespace Drupal\system\Tests\Installer;
+
+use Drupal\simpletest\InstallerTestBase;
+
+/**
+ * Tests the installer with an existing Drupal installation.
+ *
+ * @group Installer
+ */
+class InstallerExistingInstallationTest extends InstallerTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Verifies that Drupal can't be reinstalled while an existing installation is
+   * available.
+   */
+  public function testInstaller() {
+    // Verify that Drupal can't be immediately reinstalled.
+    $this->drupalGet($GLOBALS['base_url'] . '/core/install.php');
+    $this->assertRaw('Drupal already installed');
+
+    // Delete settings.php and attempt to reinstall again.
+    unlink($this->siteDirectory . '/settings.php');
+    $this->drupalGet($GLOBALS['base_url'] . '/core/install.php');
+    $this->setUpLanguage();
+    $this->setUpProfile();
+    $this->setUpSettings();
+    $this->assertRaw('Drupal already installed');
+  }
+
+}