diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index c14ab53215995a4bbc13ca2e03044cb1d5bd24db..1109452a69619de630826c738812ffd34185caa4 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -387,6 +387,13 @@ function conf_path($require_settings = TRUE, $reset = FALSE) {
     return $conf;
   }
 
+  // Check for a simpletest override.
+  if ($simpletest_conf_path = _drupal_simpletest_conf_path()) {
+    $conf = $simpletest_conf_path;
+    return $conf;
+  }
+
+  // Otherwise, use the normal $conf_path.
   $script_name = $_SERVER['SCRIPT_NAME'];
   if (!$script_name) {
     $script_name = $_SERVER['SCRIPT_FILENAME'];
@@ -396,6 +403,50 @@ function conf_path($require_settings = TRUE, $reset = FALSE) {
   return $conf;
 }
 
+/**
+ * Determines whether to use an overridden value for conf_path().
+ *
+ * Simpletest may provide a secondary, test-specific settings.php file to load
+ * after the primary one used by the parent site and override its variables.
+ * - If the child settings.php does not override $conf_path, then this function
+ * returns FALSE and conf_path() returns the directory of the primary
+ * settings.php.
+ * - If the child settings.php does override $conf_path, then
+ * _drupal_load_test_overrides() sets the 'simpletest_conf_path' setting, and
+ * this function returns that to conf_path(), causing installations and
+ * upgrades to act on that one.
+ *
+ * @return string|false
+ *   The overridden $conf_path, or FALSE if the $conf_path should not currently
+ *   be overridden.
+ *
+ * @see conf_path()
+ * @see _drupal_load_test_overrides()
+ */
+function _drupal_simpletest_conf_path() {
+  // Ensure that the settings object is available. conf_path() is called once
+  // before the Settings class is included, and at that point it should still
+  // load the primary $conf_path. See drupal_settings_initialize().
+  if (!class_exists('Drupal\Component\Utility\Settings', FALSE)) {
+    return FALSE;
+  }
+
+  // If no $simpletest_conf_path is set, use the normal $conf_path.
+  if (!($simpletest_conf_path = settings()->get('simpletest_conf_path'))) {
+    return FALSE;
+  }
+
+  // Ensure that this is actually a simpletest request. We can't check this
+  // before settings.php is loaded.
+  if (!drupal_valid_test_ua()) {
+    return FALSE;
+  }
+
+  // When the $simpletest_conf_path is set in a valid test request,
+  // return that path.
+  return $simpletest_conf_path;
+}
+
 /**
  * Finds the appropriate configuration directory for a given host and path.
  *
@@ -474,11 +525,7 @@ function find_conf_path($http_host, $script_name, $require_settings = TRUE) {
 function config_get_config_directory($type = CONFIG_ACTIVE_DIRECTORY) {
   global $config_directories;
 
-  if ($test_prefix = drupal_valid_test_ua()) {
-    // @see Drupal\simpletest\WebTestBase::setUp()
-    $path = conf_path() . '/files/simpletest/' . substr($test_prefix, 10) . '/config_' . $type;
-  }
-  elseif (!empty($config_directories[$type])) {
+  if (!empty($config_directories[$type])) {
     // Allow a configuration directory path to be outside of webroot.
     if (empty($config_directories[$type]['absolute'])) {
       $path = conf_path() . '/files/' . $config_directories[$type]['path'];
@@ -2566,6 +2613,7 @@ function drupal_valid_test_ua($new_prefix = NULL) {
     // and the HMAC must match.
     if ($time_diff >= 0 && $time_diff <= 5 && $hmac == drupal_hmac_base64($check_string, $key)) {
       $test_prefix = $prefix;
+      _drupal_load_test_overrides($test_prefix);
       return $test_prefix;
     }
   }
@@ -2574,6 +2622,43 @@ function drupal_valid_test_ua($new_prefix = NULL) {
   return $test_prefix;
 }
 
+/**
+ * Overrides low-level and environment-specific configuration.
+ *
+ * Very strictly for internal use only.
+ *
+ * Loads settings.php from the simpletest public files directory. These files
+ * can change the global $conf, the global $config_directories, the return
+ * value of conf_path(), and settings().
+ *
+ * @param string $test_prefix
+ *   The simpletest prefix.
+ */
+function _drupal_load_test_overrides($test_prefix) {
+  global $conf, $config_directories;
+
+  // Do not use the parent site's config directories. Use only the child site's.
+  // @see Drupal\simpletest\TestBase::prepareConfigDirectories()
+  $path_prefix = 'simpletest/' . substr($test_prefix, 10);
+  $config_directories = array();
+  foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) {
+    $config_directories[$type] = array('path' => $path_prefix . '/config_' . $type);
+  }
+
+  // Check for and load a settings.php file in the simpletest files directory.
+  $filename = conf_path() . '/files/' . $path_prefix . '/settings.php';
+  if (file_exists($filename)) {
+    $settings = settings()->getAll();
+    $conf_path = &drupal_static('conf_path');
+    // This can override $conf, $conf_path, $settings, and $config_directories.
+    include $filename;
+    // Keep the overriden $conf_path alive across drupal_static_reset() calls.
+    // @see conf_path()
+    $settings['simpletest_conf_path'] = $conf_path;
+    new Settings($settings);
+  }
+}
+
 /**
  * Generates a user agent string with a HMAC and timestamp for simpletest.
  */
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index 5cca38afd1d58186653c1218126f0626159eede1..4e7b6b1d4893d0bd1453d869c071fcc2e05bbf8a 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -858,6 +858,36 @@ protected function setUp() {
     $this->setup = TRUE;
   }
 
+  /**
+   * Writes a test-specific settings.php file for the child site.
+   *
+   * The child site loads this after the parent site's settings.php, so settings
+   * here override those.
+   *
+   * @param $settings An array of settings to write out, in the format expected
+   *   by drupal_rewrite_settings().
+   *
+   * @see _drupal_load_test_overrides()
+   * @see drupal_rewrite_settings()
+   */
+  protected function writeSettings($settings) {
+    // drupal_rewrite_settings() sets the in-memory global variables in addition
+    // to writing the file. We'll want to restore the original globals.
+    foreach (array_keys($settings) as $variable_name) {
+      $original_globals[$variable_name] = isset($GLOBALS[$variable_name]) ? $GLOBALS[$variable_name] : NULL;
+    }
+
+    include_once DRUPAL_ROOT . '/core/includes/install.inc';
+    $filename = $this->public_files_directory . '/settings.php';
+    file_put_contents($filename, "<?php\n");
+    drupal_rewrite_settings($settings, $filename);
+
+    // Restore the original globals.
+    foreach ($original_globals as $variable_name => $value) {
+      $GLOBALS[$variable_name] = $value;
+    }
+  }
+
   /**
    * Reset all data structures after having enabled new modules.
    *
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalAnonymousUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalAnonymousUpgradePathTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..01ce148d5d57fd1eabe9c8521cda7b4bdda2ce5b
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalAnonymousUpgradePathTest.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Upgrade\BareMinimalAnonymousUpgradePathTest.
+ */
+
+namespace Drupal\system\Tests\Upgrade;
+
+/**
+ * Tests the upgrade path without prior creation of config directions.
+ */
+class BareMinimalAnonymousUpgradePathTest extends BareMinimalUpgradePathTest {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Basic minimal profile upgrade, free access',
+      'description' => 'Basic upgrade path tests for a minimal profile install with a bare database and update_free_access set to TRUE.',
+      'group' => 'Upgrade path',
+    );
+  }
+
+  /**
+   * Overrides \Drupal\system\Tests\Upgrade\UpgradePathTestBase::setUp().
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Override $update_free_access in settings.php to allow the anonymous user
+    // to run updates.
+    $settings['settings']['update_free_access'] = (object) array(
+      'value' => TRUE,
+      'required' => TRUE,
+    );
+    $this->writeSettings($settings);
+  }
+
+  /**
+   * Overrides \Drupal\system\Tests\Upgrade\UpgradePathTestBase::prepareD8Session().
+   */
+  protected function prepareD8Session() {
+    // There is no active session, so nothing needs to be done here.
+  }
+
+  /**
+   * Overrides \Drupal\system\Tests\Upgrade\UpgradePathTestBase::assertSessionKept().
+   */
+  protected function finishUpgradeSession() {
+    // There is no active session, so nothing needs to be done here.
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalNoConfigUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalNoConfigUpgradePathTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..00feb8906ee1d942c5726cbe5197e885cbb36c31
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalNoConfigUpgradePathTest.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Upgrade\BareMinimalNoConfigUpgradePathTest.
+ */
+
+namespace Drupal\system\Tests\Upgrade;
+
+/**
+ * Tests the database upgrade path without creating config directories.
+ */
+class BareMinimalNoConfigUpgradePathTest extends BareMinimalUpgradePathTest {
+
+  public static function getInfo() {
+    return array(
+      'name'  => 'Basic minimal profile upgrade, no config',
+      'description'  => 'Basic upgrade path tests for a minimal profile install with a bare database and config directory not pre-created.',
+      'group' => 'Upgrade path',
+    );
+  }
+
+  /**
+   * Overrides \Drupal\system\Tests\Upgrade\UpgradePathTestBase::setUp().
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Override $conf_path and $config_directories in settings.php.
+    $settings['conf_path'] = (object) array(
+      'value' => $this->public_files_directory,
+      'required' => TRUE,
+    );
+    $settings['config_directories'] = (object) array(
+      'value' => array(),
+      'required' => TRUE,
+    );
+    $this->writeSettings($settings);
+  }
+
+  /**
+   * Overrides \Drupal\system\Tests\Upgrade\UpgradePathTestBase::refreshVariables().
+   */
+  protected function refreshVariables() {
+    // Refresh the variables only if the site was already upgraded.
+    if ($this->upgradedSite) {
+      // update.php puts the new, randomized config directries in this file.
+      include $this->public_files_directory . '/settings.php';
+      $GLOBALS['config_directories'] = array();
+      foreach ($config_directories as $type => $data) {
+        // update.php runs as the child site, so writes the paths relative to
+        // that "$conf_path/files", but here, we're running as the parent site,
+        // so need to make the paths relative to our "conf_path()/files".
+        //
+        // Example:
+        // - Parent site conf_path(): 'sites/default'
+        // - Child site $conf_path: 'sites/default/files/simpletest/123456'
+        // - Child site $data['path']: 'config_xyz'
+        // - Desired result: 'simpletest/123456/files/config_xyz'
+        //
+        // @see config_get_config_directory()
+        $GLOBALS['config_directories'][$type]['path'] = substr($conf_path, strlen(conf_path() . '/files/')) . '/files/' . $data['path'];
+      }
+      parent::refreshVariables();
+    }
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalUpgradePathTest.php
index 9c51bee19953a000bab4ef3d7e819d7a38ff068d..b6feee4f1f0836034c907661c65647e7357aecdc 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalUpgradePathTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalUpgradePathTest.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Definition of Drupal\system\Tests\Upgrade\BareMinimalUpgradePathTest.
+ * Contains \Drupal\system\Tests\Upgrade\BareMinimalUpgradePathTest.
  */
 
 namespace Drupal\system\Tests\Upgrade;
@@ -16,6 +16,7 @@
  * content) so that an upgrade from of a site under this profile may be tested.
  */
 class BareMinimalUpgradePathTest extends UpgradePathTestBase {
+
   public static function getInfo() {
     return array(
       'name'  => 'Basic minimal profile upgrade path, bare database',
@@ -43,12 +44,7 @@ public function testBasicMinimalUpgrade() {
     $this->assertResponse(200);
 
     // Verify that we are still logged in.
-    $this->drupalGet('user');
-    $this->clickLink(t('Edit'));
-    $this->assertEqual($this->getUrl(), url('user/1/edit', array('absolute' => TRUE)), 'We are still logged in as admin at the end of the upgrade.');
-
-    // Logout and verify that we can login back in with our initial password.
-    $this->drupalLogout();
+    $this->finishUpgradeSession();
     $this->drupalLogin((object) array(
       'uid' => 1,
       'name' => 'admin',
@@ -95,4 +91,14 @@ public function testBasicMinimalUpgrade() {
     $this->assertEqual(array('default' => 'Drupal\Core\Mail\PhpMail'), config('system.mail')->get('interface'), 'Default mail configuration set.');
   }
 
+  /**
+   * Asserts that the session was kept during update. Also, log out.
+   */
+  protected function finishUpgradeSession() {
+    $this->drupalGet('user');
+    $this->clickLink(t('Edit'));
+    $this->assertEqual($this->getUrl(), url('user/1/edit', array('absolute' => TRUE)), 'We are still logged in as admin at the end of the upgrade.');
+    $this->drupalLogout();
+  }
+
 }