diff --git a/core/core.services.yml b/core/core.services.yml
index 6310c21..2925d99 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -298,7 +298,7 @@ services:
       - { name: event_subscriber }
   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', '@profile_handler']
     lazy: true
   config.storage:
     class: Drupal\Core\Config\CachedStorage
@@ -506,6 +506,9 @@ services:
       - { name: module_install.uninstall_validator }
     arguments: ['@string_translation']
     lazy: true
+  profile_handler:
+    class: Drupal\Core\Extension\ProfileHandler
+    arguments: ['@app.root', '@info_parser']
   theme_handler:
     class: Drupal\Core\Extension\ThemeHandler
     arguments: ['@app.root', '@config.factory', '@module_handler', '@state', '@info_parser']
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 1019270..c065338 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -21,6 +21,7 @@
 use Drupal\Core\StringTranslation\Translator\FileTranslation;
 use Drupal\Core\StackMiddleware\ReverseProxyMiddleware;
 use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Core\Extension\ProfileHandler;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Url;
 use Drupal\language\Entity\ConfigurableLanguage;
@@ -439,6 +440,13 @@ function install_begin_request($class_loader, &$install_state) {
     if (isset($install_state['profile_info']['distribution']['install']['theme'])) {
       $install_state['theme'] = $install_state['profile_info']['distribution']['install']['theme'];
     }
+    // Ensure all profile directories are registered.
+    $profile_handler = \Drupal::service('profile_handler');
+    $profiles = $profile_handler->getProfiles($profile);
+    $profile_directories = array_map(function($extension) {
+      return $extension->getPath();
+    }, $profiles);
+    $listing->setProfileDirectories($profile_directories);
   }
 
   // Use the language from the profile configuration, if available, to override
@@ -1550,7 +1558,11 @@ function install_profile_themes(&$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);
+  $profile_handler = \Drupal::service('profile_handler');
+  $profiles = $profile_handler->getProfiles();
+
+  // Install all the profiles.
+  \Drupal::service('module_installer')->install(array_keys($profiles), FALSE);
   // Install all available optional config. During installation the module order
   // is determined by dependencies. If there are no dependencies between modules
   // then the order in which they are installed is dependent on random factors
diff --git a/core/includes/install.inc b/core/includes/install.inc
index 3a9c2bc..29e6cef 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -11,6 +11,7 @@
 use Drupal\Component\Utility\OpCodeCache;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Core\Extension\ModuleHandler;
 use Drupal\Core\Site\Settings;
 
 /**
@@ -1029,6 +1030,14 @@ function drupal_check_module($module) {
  *   - install: Optional parameters to override the installer:
  *     - theme: The machine name of a theme to use in the installer instead of
  *       Drupal's default installer theme.
+ * - base profile: Existence of this key denotes that the installation profile
+ *   depends on a parent installation profile.
+ *   - name: The shortname of the base installation profile.
+ *   - excluded_dependencies: An array of shortnames of other modules that have
+ *     to be excluded from the base profile requirements. This allows e.g. to
+ *     disable a demo module that would be installed by the base profile.
+ *   If there are no excluded_dependencies, a shortcut of "base profile: name"
+ *   can be used.
  *
  * Note that this function does an expensive file system scan to get info file
  * information for dependencies. If you only need information from the info
@@ -1073,6 +1082,27 @@ function install_profile_info($profile, $langcode = 'en') {
 
     $locale = !empty($langcode) && $langcode != 'en' ? array('locale') : array();
 
+    // Get the base profile chain.
+    $base_profile_name = !empty($info['base profile']['name']) ? $info['base profile']['name'] :
+      (!empty($info['base profile']) ? $info['base profile'] : '');
+    if ($base_profile_name) {
+      $base_profile = install_profile_info($base_profile_name, $langcode);
+
+      // Ensure all dependencies are cleanly merged.
+      $info['dependencies'] = array_merge($info['dependencies'], $base_profile['dependencies']);
+      // If there are dependency excludes from the base apply them now.
+      if (!empty($info['base profile']['excluded_dependencies'])) {
+        $info['dependencies'] = array_diff($info['dependencies'], $info['base profile']['excluded_dependencies']);
+      }
+      // Ensure there's no circular dependency.
+      $info['dependencies'] = array_diff($info['dependencies'], array($profile));
+    }
+
+    // Ensure the same dependency notation as in modules can be used.
+    array_walk($info['dependencies'], function(&$dependency) {
+      $dependency = ModuleHandler::parseDependency($dependency)['name'];
+    });
+
     $info['dependencies'] = array_unique(array_merge($required, $info['dependencies'], $locale));
 
     $cache[$profile][$langcode] = $info;
diff --git a/core/lib/Drupal/Core/Config/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php
index 97c3688..12e316e 100644
--- a/core/lib/Drupal/Core/Config/ConfigInstaller.php
+++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php
@@ -7,6 +7,7 @@
 use Drupal\Core\Config\Entity\ConfigDependencyManager;
 use Drupal\Core\Config\Entity\ConfigEntityDependency;
 use Drupal\Core\Site\Settings;
+use Drupal\Core\Extension\ProfileHandlerInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 class ConfigInstaller implements ConfigInstallerInterface {
@@ -54,6 +55,13 @@ class ConfigInstaller implements ConfigInstallerInterface {
   protected $sourceStorage;
 
   /**
+   * ProfileHandler
+   *
+   * @var \Drupal\Core\Extension\ProfileHandler
+   */
+  protected $profileHandler;
+
+  /**
    * Is configuration being created as part of a configuration sync.
    *
    * @var bool
@@ -73,13 +81,16 @@ class ConfigInstaller implements ConfigInstallerInterface {
    *   The configuration manager.
    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
    *   The event dispatcher.
+   * @param \Drupal\Core\Extension\ProfileHandlerInterface $profile_handler
+   *   The profile handler.
    */
-  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, ProfileHandlerInterface $profile_handler) {
     $this->configFactory = $config_factory;
     $this->activeStorages[$active_storage->getCollectionName()] = $active_storage;
     $this->typedConfig = $typed_config;
     $this->configManager = $config_manager;
     $this->eventDispatcher = $event_dispatcher;
+    $this->profileHandler = $profile_handler;
   }
 
   /**
@@ -462,7 +473,8 @@ public function checkConfigurationToInstall($type, $name) {
 
     // 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()) {
+    $profiles = $this->profileHandler->getProfiles();
+    if (!isset($profiles[$name])) {
       // Throw an exception if the module being installed contains configuration
       // that already exists. Additionally, can not continue installing more
       // modules because those may depend on the current module being installed.
diff --git a/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php b/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php
index 14e80dd..f748f15 100644
--- a/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php
+++ b/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php
@@ -86,13 +86,13 @@ protected function getAllFolders() {
         $modules = $extensions['module'];
         // Remove the install profile as this is handled later.
         unset($modules[$install_profile]);
-        $profile_list = $listing->scan('profile');
-        if ($profile && isset($profile_list[$profile])) {
+        $profiles = $listing->scan('profile');
+        foreach ($profiles as $profile_name => $profile_object) {
           // Prime the drupal_get_filename() static cache with the profile info
           // file location so we can use drupal_get_path() on the active profile
           // during the module scan.
           // @todo Remove as part of https://www.drupal.org/node/2186491
-          drupal_get_filename('profile', $profile, $profile_list[$profile]->getPathname());
+          drupal_get_filename('profile', $profile_name, $profile_object->getPathname());
         }
         $module_list_scan = $listing->scan('module');
         $module_list = array();
@@ -118,11 +118,9 @@ protected function getAllFolders() {
         // this by replacing the config file path from the module/theme with the
         // install profile version if there are any duplicates.
         if (isset($profile)) {
-          if (!isset($profile_list)) {
-            $profile_list = $listing->scan('profile');
-          }
-          if (isset($profile_list[$profile])) {
-            $profile_folders = $this->getComponentNames(array($profile_list[$profile]));
+          $profiles = \Drupal::service('profile_handler')->getProfiles($profile);
+          foreach ($profiles as $extension) {
+            $profile_folders = $this->getComponentNames(array($extension));
             $this->folders = $profile_folders + $this->folders;
           }
         }
diff --git a/core/lib/Drupal/Core/Config/InstallStorage.php b/core/lib/Drupal/Core/Config/InstallStorage.php
index bd9bf30..8112d05 100644
--- a/core/lib/Drupal/Core/Config/InstallStorage.php
+++ b/core/lib/Drupal/Core/Config/InstallStorage.php
@@ -155,16 +155,18 @@ protected function getAllFolders() {
       // yet because the system module may not yet be enabled during install.
       // @todo Remove as part of https://www.drupal.org/node/2186491
       $listing = new ExtensionDiscovery(\Drupal::root());
-      if ($profile = drupal_get_profile()) {
-        $profile_list = $listing->scan('profile');
-        if (isset($profile_list[$profile])) {
-          // Prime the drupal_get_filename() static cache with the profile info
-          // file location so we can use drupal_get_path() on the active profile
-          // during the module scan.
-          // @todo Remove as part of https://www.drupal.org/node/2186491
-          drupal_get_filename('profile', $profile, $profile_list[$profile]->getPathname());
-          $this->folders += $this->getComponentNames(array($profile_list[$profile]));
-        }
+      $profiles = $listing->scan('profile');
+      foreach ($profiles as $profile_name => $extension) {
+        // Prime the drupal_get_filename() static cache with the profile info
+        // file location so we can use drupal_get_path() on the active profile
+        // during the module scan.
+        // @todo Remove as part of https://www.drupal.org/node/2186491
+        drupal_get_filename('profile', $profile_name, $extension->getPathname());
+      }
+      // Now that we can fetch the path, get dependent profiles and add the extensions
+      $profiles = \Drupal::service('profile_handler')->getProfiles();
+      foreach ($profiles as $extension) {
+        $this->folders += $this->getComponentNames(array($extension));
       }
       // @todo Remove as part of https://www.drupal.org/node/2186491
       $this->folders += $this->getComponentNames($listing->scan('module'));
diff --git a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
index 0d9283c..8ba042c 100644
--- a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
+++ b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
@@ -7,6 +7,7 @@
 use Drupal\Core\Extension\Discovery\RecursiveExtensionFilterIterator;
 use Drupal\Core\Site\Settings;
 use Symfony\Component\HttpFoundation\Request;
+use Drupal\Core\Extension\ProfileHandler;
 
 /**
  * Discovers available extensions in the filesystem.
@@ -248,8 +249,21 @@ public function setProfileDirectoriesFromSettings() {
     // In case both profile directories contain the same extension, the actual
     // profile always has precedence.
     if ($profile) {
-      $this->profileDirectories[] = drupal_get_path('profile', $profile);
+      // ExtensionDiscovery can be called without a service container.
+      // (@drupalKernel::moduleData) so check if profile_handler is available.
+      if (\Drupal::hasService('profile_handler')) {
+        $profile_handler = \Drupal::service('profile_handler');
+        $profiles = $profile_handler->getProfiles($profile);
+        $profile_directories = array_map(function($extension) {
+          return $extension->getPath();
+        }, $profiles);
+        $this->profileDirectories = array_merge($profile_directories, $this->profileDirectories);
+      }
+      else {
+        $this->profileDirectories[] = drupal_get_path('profile', $profile);
+      }
     }
+
     return $this;
   }
 
diff --git a/core/lib/Drupal/Core/Extension/ProfileHandler.php b/core/lib/Drupal/Core/Extension/ProfileHandler.php
new file mode 100644
index 0000000..1e078b7
--- /dev/null
+++ b/core/lib/Drupal/Core/Extension/ProfileHandler.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Drupal\Core\Extension;
+
+use Drupal\Core\Site\Settings;
+
+/**
+ * Class that manages profiles in a Drupal installation.
+ */
+class ProfileHandler implements ProfileHandlerInterface {
+
+  /**
+   * Static state cache.
+   *
+   * @var array
+   */
+  protected static $cache = array();
+
+  /**
+   * The app root.
+   *
+   * @var string
+   */
+  protected $root;
+
+  /**
+   * The info parser to parse the profile.info.yml files.
+   *
+   * @var \Drupal\Core\Extension\InfoParserInterface
+   */
+  protected $infoParser;
+
+  /**
+   * Constructs a new ProfileHandler.
+   *
+   * @param string $root
+   *   The app root.
+   * @param \Drupal\Core\Extension\InfoParserInterface $info_parser
+   *   The info parser to parse the profile.info.yml files.
+   */
+  public function __construct($root, InfoParserInterface $info_parser) {
+    $this->root = $root;
+    $this->infoParser = $info_parser;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getProfiles($profile = NULL) {
+    static $weight;
+
+    if (empty($profile)) {
+      $profile = drupal_get_profile();
+    }
+    if (!isset(static::$cache[$profile])) {
+      $profiles = array();
+      // Starting weight for profiles ensures their hooks run last.
+      $weight = 1000;
+      // Check if a valid profile name was given.
+      if (!empty($profile)) {
+        // We can't use install_profile_info() because that could trigger an
+        // endless loop. So we read this on our own.
+        $profile_path = drupal_get_path('profile', $profile);
+        $profile_file = $profile_path . "/$profile.info.yml";
+        $profile_info = $this->infoParser->parse($profile_file);
+        $type = $profile_info['type'];
+
+        // Check of the profile has a base profile and if so add it - recursion.
+        $base_profile_name = !empty($profile_info['base profile']['name']) ? $profile_info['base profile']['name'] :
+          (!empty($profile_info['base profile']) ? $profile_info['base profile'] : '');
+        if ($base_profile_name) {
+          $profiles += $this->getProfiles($base_profile_name);
+          $weight++;
+        }
+
+        $filename = file_exists($profile_path . "/$profile.$type") ? "$profile.$type" : NULL;
+        $extension = new Extension($this->root, $type, $profile_file, $filename);
+
+        $info_defaults = array();
+
+        // Installation profiles are hidden by default, unless explicitly specified
+        // otherwise in the .info.yml file.
+        $info_defaults['hidden'] = isset($profile_info['hidden']) ? $profile_info['hidden'] : TRUE;
+
+        $extension->info = $profile_info + $info_defaults;
+        $extension->weight = $weight;
+        $extension->origin = '';
+
+        // See @1356276-192.  Issue in System::_system_rebuild_module_data,
+        // The $extension->info gets changed, so we need to save the original
+        // parse result elsewhere so it can be used to set profile defaults.
+        $extension->info_defaults = $info_defaults;
+
+        // Add requested profile as last one.
+        $profiles[$profile] = $extension;
+      }
+      static::$cache[$profile] = $profiles;
+    }
+    return static::$cache[$profile];
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Extension/ProfileHandlerInterface.php b/core/lib/Drupal/Core/Extension/ProfileHandlerInterface.php
new file mode 100644
index 0000000..a03dce0
--- /dev/null
+++ b/core/lib/Drupal/Core/Extension/ProfileHandlerInterface.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Extension;
+
+/**
+ * Interface for classes that manage profiles.
+ */
+interface ProfileHandlerInterface {
+
+  /**
+   * Returns a list of related installation profiles.
+   *
+   * @param string $profile
+   *   Name of profile.  If none is specified, use the current profile.
+   *
+   * @return array
+   *   List of installation profile extensions keyed by profile name
+   *   in descending order of their dependencies.
+   *   (parent profiles first, main profile last)
+   */
+  public function getProfiles($profile = NULL);
+
+}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 2f627be..a06fa1e 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -963,23 +963,20 @@ function _system_rebuild_module_data() {
   // module scan as the module scan requires knowing what the active profile is.
   // @todo Remove as part of https://www.drupal.org/node/2186491.
   $profiles = $listing->scan('profile');
-  $profile = drupal_get_profile();
-  if ($profile && isset($profiles[$profile])) {
+  foreach ($profiles as $profile_name => $extension) {
     // Prime the drupal_get_filename() static cache with the profile info file
     // location so we can use drupal_get_path() on the active profile during
     // the module scan.
     // @todo Remove as part of https://www.drupal.org/node/2186491.
-    drupal_get_filename('profile', $profile, $profiles[$profile]->getPathname());
+    drupal_get_filename('profile', $profile_name, $extension->getPathname());
   }
 
   // Find modules.
   $modules = $listing->scan('module');
-  // Include the installation profile in modules that are loaded.
-  if ($profile) {
-    $modules[$profile] = $profiles[$profile];
-    // Installation profile hooks are always executed last.
-    $modules[$profile]->weight = 1000;
-  }
+
+  // Find profiles.
+  $profiles = \Drupal::service('profile_handler')->getProfiles();
+  $modules = array_merge($modules, $profiles);
 
   // Set defaults for module info.
   $defaults = array(
@@ -994,6 +991,12 @@ function _system_rebuild_module_data() {
   foreach ($modules as $key => $module) {
     // Look for the info file.
     $module->info = \Drupal::service('info_parser')->parse($module->getPathname());
+    // Add any defaults stored in the extension (see ProfileHandler)
+    if (!empty($module->info_defaults)) {
+      $module->info = $module->info + $module->info_defaults;
+    }
+    else {
+    }
 
     // Add the info file modification time, so it becomes available for
     // contributed modules to use for ordering module lists.
@@ -1002,12 +1005,6 @@ function _system_rebuild_module_data() {
     // Merge in defaults and save.
     $modules[$key]->info = $module->info + $defaults;
 
-    // Installation profiles are hidden by default, unless explicitly specified
-    // otherwise in the .info.yml file.
-    if ($key == $profile && !isset($modules[$key]->info['hidden'])) {
-      $modules[$key]->info['hidden'] = TRUE;
-    }
-
     // Invoke hook_system_info_alter() to give installed modules a chance to
     // modify the data in the .info.yml files if necessary.
     // @todo Remove $type argument, obsolete with $module->getType().
@@ -1021,7 +1018,8 @@ function _system_rebuild_module_data() {
     _system_rebuild_module_data_ensure_required($module, $modules);
   }
 
-
+  // This must be done after _system_rebuild_module_data_ensure_required().
+  $profile = drupal_get_profile();
   if ($profile && isset($modules[$profile])) {
     // The installation profile is required, if it's a valid module.
     $modules[$profile]->info['required'] = TRUE;
