Is it possible to make a "private" distribution not listed on Drupal.org? For a customer we should do a bunch of sites build with Drupal. Multisite is not possible because of different hosting solutions.

Would creating a custom distribution work out for that case? Is it possible to make a "private" distribution not listed on Drupal.org?
On drupal.org, a distribution is a single download containing Drupal core, contributed modules, themes, and pre-defined configuration. You could create a distribution locally, using the same tools used on drupal.org to create a distribution, but that would involve creating a drupal-org.make file using drush make, which is a command removed from Drush.

Instead, as Drush itself suggests, you should use Composer. Create a composer.json file, locally, and add all the dependencies (including modules) you need with composer require. If then you need, for example, to create specific content types, roles, taxonomy terms, or other entities, you could create an installation profile, as described in How to Write a Drupal 8 Installation Profile. Using an installation profile doesn't work for already created sites, but for sites you still have to create, a custom installation profile helps in building a site.  

As example of what an installation profile can do, see the content of the core/profiles/standard directory, in particular the config directory, which contains the configuration files necessary to create the default entities you normally use on the default Drupal installation. The install/node.type.page.yml file is a configuration file that defines a node content type. (The filename is important for Drupal to understand it's a node content type.)

langcode: en
status: true
dependencies: {  }
name: 'Basic page'
type: page
description: 'Use &lt;em&gt;basic pages&lt;/em&gt; for your static content, such as an ''About us'' page.'
help: ''
new_revision: true
preview_mode: 1
display_submitted: false


An installation profile can have dependencies and a list of core modules that needs to be installed but that aren't dependencies of the profile (see the standard.info.yml file), implement hook_install() (see the standard.install file) and other hooks a module can implements, including the one to alter the site configuration form shown during the installation of Drupal (see the standard.profile file).

function standard_install() {
  // Set front page to "node".
  \Drupal::configFactory()-&gt;getEditable('system.site')-&gt;set('page.front', '/node')-&gt;save(TRUE);

  // Allow visitor account creation with administrative approval.
  $user_settings = \Drupal::configFactory()-&gt;getEditable('user.settings');
  $user_settings-&gt;set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)-&gt;save(TRUE);

  // Enable default permissions for system roles.
  user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access comments']);
  user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, ['access comments', 'post comments', 'skip comment approval']);

  // Assign user 1 the "administrator" role.
  $user = User::load(1);
  $user-&gt;roles[] = 'administrator';
  $user-&gt;save();

  // We install some menu links, so we have to rebuild the router, to ensure the
  // menu links are valid.
  \Drupal::service('router.builder')-&gt;rebuildIfNeeded();

  // Enable the Contact link in the footer menu.
  /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
  $menu_link_manager = \Drupal::service('plugin.manager.menu.link');
  $menu_link_manager-&gt;updateDefinition('contact.site_page', ['enabled' =&gt; TRUE]);

  user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
  user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, ['access site-wide contact form']);

  // Allow authenticated users to use shortcuts.
  user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, ['access shortcuts']);

  // Populate the default shortcut set.
  $shortcut = Shortcut::create([
    'shortcut_set' =&gt; 'default',
    'title' =&gt; t('Add content'),
    'weight' =&gt; -20,
    'link' =&gt; ['uri' =&gt; 'internal:/node/add'],
  ]);
  $shortcut-&gt;save();

  $shortcut = Shortcut::create([
    'shortcut_set' =&gt; 'default',
    'title' =&gt; t('All content'),
    'weight' =&gt; -19,
    'link' =&gt; ['uri' =&gt; 'internal:/admin/content'],
  ]);
  $shortcut-&gt;save();

  // Allow all users to use search.
  user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['search content']);
  user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, ['search content']);

  // Enable the admin theme.
  \Drupal::configFactory()-&gt;getEditable('node.settings')-&gt;set('use_admin_theme', TRUE)-&gt;save(TRUE);
}


Installation profiles cannot be changed once Drupal is installed. If you need to use a different installation profile, you need to re-install Drupal. If you aren't creating sites ex-novo, instead of an installation profile, you can create a custom module that you install on existing Drupal sites. A module can still create new entities or entity bundles via configuration files, have code executed when it's installed (via hook_install()), and define its dependencies. See, for example, what the Forum module does, for example with its config/optional/node.type.forum.yml file.

langcode: en
status: true
dependencies:
  enforced:
    module:
      - forum
name: 'Forum topic'
type: forum
description: 'A &lt;em&gt;forum topic&lt;/em&gt; starts a new discussion thread within a forum.'
help: ''
new_revision: false
preview_mode: 1
display_submitted: true


There are some light differences between a configuration file used from an installation profile and a configuration file. Those differences allow a module to be uninstalled and re-installed, and have its entities (or entity bundles) re-created when the module is re-installed. (Doing differently, re-installing the module would shown an error complaining about the entity existing already.)