diff --git a/core/core.services.yml b/core/core.services.yml
index 276e19d..759920e 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -290,7 +290,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
@@ -498,6 +498,9 @@ services:
       - { name: module_install.uninstall_validator }
     arguments: ['@string_translation']
     lazy: true
+  profile_handler:
+    class: Drupal\Core\Extension\ProfileHandler
+    arguments: ['@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..a5eb9dd 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,10 @@ 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');
+    $profile_directories = $profile_handler->getProfileDependencies($profile);
+    $listing->setProfileDirectories($profile_directories);
   }
 
   // Use the language from the profile configuration, if available, to override
@@ -1550,7 +1555,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->getProfileDependencies();
+
+  // 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..43a0efc 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->getProfileDependencies();
+    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..feb255b 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();
@@ -121,9 +121,12 @@ protected function getAllFolders() {
           if (!isset($profile_list)) {
             $profile_list = $listing->scan('profile');
           }
-          if (isset($profile_list[$profile])) {
-            $profile_folders = $this->getComponentNames(array($profile_list[$profile]));
-            $this->folders = $profile_folders + $this->folders;
+          $profile_names = \Drupal::service('profile_handler')->getProfileDependencies($profile);
+          foreach ($profile_names as $profile_name => $profile_path) {
+            if (isset($profile_list[$profile_name])) {
+              $profile_folders = $this->getComponentNames(array($profile_list[$profile_name]));
+              $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..d68e028 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 => $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_name, $profile_object->getPathname());
+      }
+      // Now that we can fetch the path, get dependent profiles and add the extensions
+      $profile_names = \Drupal::service('profile_handler')->getProfileDependencies();
+      foreach ($profile_names as $profile_name => $profile_path) {
+        $this->folders += $this->getComponentNames(array($profiles[$profile_name]));
       }
       // @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..a71d413 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,17 @@ 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');
+        $this->profileDirectories = array_merge($profile_handler->getProfileDependencies($profile), $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..62dcb2d
--- /dev/null
+++ b/core/lib/Drupal/Core/Extension/ProfileHandler.php
@@ -0,0 +1,67 @@
+<?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 info parser to parse the profile.info.yml files.
+   *
+   * @var \Drupal\Core\Extension\InfoParserInterface
+   */
+  protected $infoParser;
+
+  /**
+   * Constructs a new ProfileHandler.
+   *
+   * @param \Drupal\Core\Extension\InfoParserInterface $info_parser
+   *   The info parser to parse the profile.info.yml files.
+   */
+  public function __construct(InfoParserInterface $info_parser) {
+    $this->infoParser = $info_parser;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getProfileDependencies($profile = NULL) {
+    if (empty($profile)) {
+      $profile = drupal_get_profile();
+    }
+    if (!isset(static::$cache[$profile])) {
+      $profiles = array();
+      // 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);
+
+        // 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->getProfileDependencies($base_profile_name);
+        }
+        // Add requested profile as last one.
+        $profiles[$profile] = $profile_path;
+      }
+      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..0801c36
--- /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 dependent installation profiles paths keys by profile name
+   *   in descending order of their dependencies.
+   *   (parent profiles first, main profile last)
+   */
+  public function getProfileDependencies($profile = NULL);
+
+}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 2f627be..e7e2621 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -963,22 +963,26 @@ 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 => $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, $profiles[$profile]->getPathname());
+    drupal_get_filename('profile', $profile_name, $profile->getPathname());
   }
 
   // Find modules.
   $modules = $listing->scan('module');
+
+  $profile = drupal_get_profile();
+  $profile_names = array_keys(\Drupal::service('profile_handler')->getProfileDependencies($profile));
   // Include the installation profile in modules that are loaded.
-  if ($profile) {
-    $modules[$profile] = $profiles[$profile];
+  $weight = 1000;
+  foreach ($profile_names as $profile_name) {
+    $modules[$profile_name] = $profiles[$profile_name];
     // Installation profile hooks are always executed last.
-    $modules[$profile]->weight = 1000;
+    $modules[$profile_name]->weight = $weight;
+    $weight++;
   }
 
   // Set defaults for module info.
@@ -1004,7 +1008,7 @@ function _system_rebuild_module_data() {
 
     // Installation profiles are hidden by default, unless explicitly specified
     // otherwise in the .info.yml file.
-    if ($key == $profile && !isset($modules[$key]->info['hidden'])) {
+    if (in_array($key, $profile_names) && !isset($modules[$key]->info['hidden'])) {
       $modules[$key]->info['hidden'] = TRUE;
     }
 
