Drupal 8: Repairing A Broken Multi-Site Configuration Setup

I recently wrote a post about setting up a multi-site configuration setup using the Configuration Split module. That post was written after I did research into how to set up configuration splits and use them to create multi-site setups in Drupal. One thing I realised when doing that research was that although it's quite easy to get setup with that kind of setup, it's also easy to get it wrong and create a setup that really doesn't work.

Over the past few months I had repeatedly encountered Drupal sites built by developers that have attempted to create a multi-site setup and got it wrong. This resulted in broken configurations that don't import correctly or destroyed sites when done, nested configurations that don't work, or even install profiles that don't actually install anything. In one situation (before I started working on the site) staging configurations made their way onto the production platform, which resulted in payments being taken against a sandbox environment. A fine mess!

This has put me in the situation where I have had to rebuild these sites so that they work as a multi-site again. Consequently I have leant quite a bit recently on how to get a messy and broken configuration into a working state again. So far there are only a couple of ways out of this situation, which I will attempt to go through here.

Hard Split

A 'hard split' is where each site becomes it's own split and there is no concept of a default configuration. This is perhaps the easiest to create but does mean that the sites are then separated from the configuration of the rest of the sites. The downside is that if you want to deploy some common configuration across all of the sites you'll need to manually apply it to the hard split sites after the fact.

To get this working just set up each site, one at a time, and export it's configuration using drush cex. You then take this configuration and copy it to the site configuration directory.

You then create your configuration split that blacklists all of the configuration on the site. You can do this by selecting all modules and all other active configuration in the configuration split admin area.

Once complete you should have a main configuration directory that contains just your configuration split settings and a series of directories for your sites. Due to the fact that each site has it's on configuration you can't then create a 'default' configuration. If you try to do this you'll find the configuration split module will continuously delete your default configuration when exporting configurations for sites.

Merge Split

This entails taking the configuration from two (or more) sites and combining them together to create a default configuration. This means that each site only needs to override the things that it needs to customise.

This sounds easy, but this is by far the more complex to setup and get right if you are approaching it from this site. Once the configuration of sites gets out of sync (or are non-existent) there are a few problems that need sorting out in order to bring them back into line.

Generally speaking it's a really good idea to have a 'default' site in mind. This is a site that will be used as the parent configuration and other sites will either deviate or conform to this site. Ideally, you'll want to take one of the existing sites and use this, but you can possibly generate a default site and run with that.

Perhaps the most important thing to start off with is just a manual tweak of the configuration. For example, if one site has a module enabled but all other sites do not then uninstall that module so that the sites are more similar. This can get complex when looking at content dependent configurations like content types or fields. If you need to, then do some content editing/deleting so that you can edit/delete the content type or field in question. In extreme examples you can migrate the content from one type to another to get this working but the effort should be towards a unified configuration. Keep in mind the parent site when doing this, if possible try to bring things in line with the parent site.

With the configuration tweaked you should then export all of the configuration into separate directories, much like in the hard split strategy. The only difference here is that the configuration split files should be left in the default state for now.

Once you've done that it's time to start merging the configuration. This is done by looking at the configuration in each site and deciding what should happen with it. You can then take a few different actions.

  • If the configuration exists in the a site and not in any of the other sites the then leave it alone and add it to the config split blacklist for that site.
  • If the configuration exists in all sites and there is no difference between them, then the configuration can be placed into a default configuration area. The configuration can then be deleted from all of the sites.
  • If the configuration exists in sites and that configuration is different then add this configuration to the configuration split grey list for the effected sites and make a copy in the default configuration.
  • If the configuration exists in sites and the only difference is the uuid then we need to treat this differently. The uuid is used by the sites to identify the configuration in the site, for this reason it can't be moved without altering that uuid. The way to merge this configuration is to move the file and then to use the primary site uuid as the new uuid for all other sites. This can be done by using an update hook that can be added to an install profile or a module so that it will get triggered before the configuration import is done.

This is an example of the update hook that might be generated.

<?php

/**
 * Fix the uuid information on certain configs.
 */
function my_module_update_8001() {
  $fieldChanges = [
    'node.type.landing_page' => '2034e796-4d2c-43f0-9135-1afc93052380',
  ];

  foreach ($fieldChanges as $field => $uuid) {
    $config = \Drupal::service('config.factory')->getEditable($field);
    $config->set('uuid', $uuid)->save();
  }
}

This just shows a single configuration file change, but a real Drupal site will likely have hundreds of entries here.

The final step in this is to allow sites to ignore any configuration that they don't want. You can do this using the Configuration Ignore module, which allows sites to ignore certain parts of the configuration. This list can seem difficult to put together, but by attempting to import the config on site you can see what new configuration will be imported. From this list you can then create a configuration ignore file that contains every item of configuration that exists in the default, but doesn't exist in a site.

Here is an example of a configuration ignore file that stops a content type called "News" from being installed. This is a silly example as it doesn't take into account fields or storage for this content type, a real ignore file will probably have 50+ entries.

ignored_config_entities:
  0: node.type.news
_core:
  default_config_hash: ...

With this in place we can create another update hook that will install a configuration ignore file on a particular site. This ties in with the current status of the configuration split for that site as we don't want to accidentally install this ignore onto different sites.

/**
 * Force import the config ignore configurations for the a site.
 */
function my_module_update_8002() {
  global $config;
  if ($config['config_split.config_split.thesite']['status'] == TRUE) {
    $config_path = realpath('../config/thesite');
    $source = new FileStorage($config_path);
    $config_storage = \Drupal::service('config.storage');
    $config_storage->write('config_ignore.settings', $source->read('config_ignore.settings'));
  }
}

Manually figuring all this out can take many, many hours and will ultimately result in frustration. This is why I started to create a tool to help do this automatically. Config split merge was created when I found myself staring at a massive configuration problem and needed a way to sort it out quickly. I only had two sites to work with at the time, so the tool currently only works with two configurations. I plan on updating the tool to take a number of different sites into account and to also incorporate configuration ignore.

What Works For You?

So I've been through a couple of different examples here, but I'm really keen on seeing how other people have solved similar problems to this. I have used both methods to set up existing sites that are now live.

I'm also concerned as to why I've seen so many Drupal sites setup like this. I know it's complex to get a multi-site setup working correctly, but everything is built into Drupal to get things working correctly. I plan on writing a few more posts on Drupal configuration to show how it works with me.

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
2 + 2 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.