Configuration override system

Last updated on
9 March 2024

Drupal's configuration system handles configuration in a unified manner. By default, Drupal stores configuration data in the database, but it can be exported to YAML files, allowing the configuration to be managed by version control and deployed between environments. There are cases however when configuration values need to be overridden for specific purposes. Drupal 7 had the global $conf variable that was usually populated in settings.php with conditional override values for the configuration. A big drawback of that system was that the overrides crept into actual configuration. When a configuration form that contained overridden values was saved, the conditional override got into the actual configuration storage.

Drupal introduces a configuration override system that:

  • Maintains these overrides as temporary layers on top of the standard configuration values
  • Do not use overridden values for configuration forms
  • May store overrides with other configuration files for staging and version control support (for example, in case of language overrides; see below).

The Drupal 7 global $conf variable is renamed to $config in Drupal 8 and is activated on the configuration system by default.

Global overrides

Drupal provides the possibility of using the global $config overrides. The configuration system integrates these override values via the Drupal\Core\Config\ConfigFactory::get() implementation. When you retrieve a value from configuration, the global $config variable gets a chance to change the returned value:

// Get system site maintenance message text. This value may be overriden by
// default from global $config (as well as translations, see below).
$message = \Drupal::config('system.maintenance')->get('message');

To override configuration values in global $config for example in settings.php, reference their configuration keys:

$config['system.maintenance']['message'] = 'Sorry, our site is down now.';

For nested values, use nested array keys

$config['system.performance']['css']['preprocess'] = 0;

When using $config override outside of settings.php, use a preceding global $config;

You may find it helpful to identify the available config variables by either :

  • Browsing them using "Configuration Manager" module using the UI found at /admin/config/development/configuration/single/export
  • inspecting your sites config yml files directly,
  • or querying for them with drush.
    drush config-list
    drush config-get system.performance --include-overridden
    

Note that values overridden via $config within settings.php will not be viewable from the Drupal administration interface (until #2408549: There is no indication on configuration forms if there are overridden values is fixed, until then you can use Configuration Override Warn or Config Override Inspector) or from inspection via drush (unless you add the --include-overridden flag). The administration interface displays the values stored in configuration so that you can stage changes to other environments that don't have the overrides.

For an example of Overriding API Keys for security reasons see: Commerce Gateway Payments API Key Override

Avoiding overrides

You can fetch configuration without overrides to access the raw configuration value (such as to disable applying even the global overrides). This is very useful, for example, if you are writing a configuration form. Using an override-free environment for the form is important, so you can avoid the values creeping into your saved configuration. This is even more useful if your code is used in a multilingual environment, where values of your configuration are routinely overridden as translations.

Here are some examples of getting configuration with and without overrides.

// Get the site name, with overrides.
$site_name = \Drupal::config('system.site')->get('name');

// Get the site name without overrides.
$site_name = \Drupal::config('system.site')->getOriginal('name', FALSE);
// Note that mutable config is always override free.
$site_name = \Drupal::configFactory()->getEditable('system.site')->get('name');

You can also access the config storage directly via the config.storage service which implements StorageInterface::read(). However, this is rarely the correct way to access configuration.

Language overrides

For example, for sending an email to a user, the configuration for that should come in the language of the user, not the page. So remember the language used previously, set the proper language based on the user, do the configuration based operations and set back the language:

// Load the language_manager service
$language_manager = \Drupal::service('language_manager');

// Get the target language object
$langcode = $account->getPreferredLangcode();
$language = $language_manager->getLanguage($langcode);

// Remember original language before this operation.
$original_language = $language_manager->getConfigOverrideLanguage();
// Set the translation target language on the configuration factory.
$language_manager->setConfigOverrideLanguage($language);

$mail_config  = \Drupal::config('user.mail');

// Now send email based on $mail_config which is in the proper language.

// Set the configuration language back.
$language_manager->setConfigOverrideLanguage($original_language);

The language override system also uses configuration storage to store the overrides (unlike global $config based overrides). The language overrides are stored in files named after their base file. So if there is a language-specific override for the above-mentioneduser.mail configuration file, it would be named language.config.$langcode.user.mail. The override files are named with a language.config. prefix, then a language code, and then the original configuration key. Storing the files with regular configuration ensures configuration translations are staged and can be diff'd for changes just like the base configuration.

How do these language override files get created in the first place? Locale module integrates with system events to create translation files for shipped configuration based on the configuration schema information. See #1905152: Integrate config schema with locale, so shipped configuration is translated for background information about that. There is also the core Configuration Translation module which provides an overall user interface to translating configuration based on the schemas. It works for shipped as well as user-created configuration and works with the same language override files.

Providing overrides from modules

It is also possible to provide module level overrides from any module. While Drupal core supports global overrides as well as language-based overrides, there are use cases for many other kinds of overrides, including user role-based, context-based, domain-based, group-based, and so on. Modules can define their own criteria to do these overrides.

When the ConfigFactory collects module provided overrides, it calls any services tagged with config.factory.override:

config_example.services.yml

services:
  config_example.overrider:
    class: Drupal\config_example\Config\ConfigExampleOverrides
    tags:
      - {name: config.factory.override, priority: 5}

Set the subscriber priority to specify the priority of the overrides. Overrides with higher priority will trump those with lower priority (in case of the same config name).

src/Config/ConfigExampleOverrides.php

namespace Drupal\config_example\Config;

use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryOverrideInterface;
use Drupal\Core\Config\StorageInterface;

/**
 * Example configuration override.
 */
class ConfigExampleOverrides implements ConfigFactoryOverrideInterface {

  /**
   * {@inheritdoc}
   */
  public function loadOverrides($names) {
    $overrides = [];
    if (in_array('system.site', $names)) {
      $overrides['system.site'] = ['name' => 'Overridden site name!'];
    }
    return $overrides;
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheSuffix() {
    return 'ConfigExampleOverrider';
  }
  
  /**
   * {@inheritdoc}
   */
  public function getCacheableMetadata($name) {
    return new CacheableMetadata();
  }

  /**
   * {@inheritdoc}
   */
  public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
    return NULL;
  }

}

Configuration overrides themselves to operate at three distinct layers: language, modules and settings.php, with the last of these taking precedence. Overrides in settings.php take precedence over values provided by modules. Overrides provided by modules take precedence over language. The event subscriber priority for module overrides only sets priority versus the other module overrides, it cannot be used to set higher priority in relation to language or settings.php overrides.

Keep in mind that configuration forms in Drupal Core don't use the overridden values of the configuration. With the module override example above, you will not see "Overridden site name!" at /admin/config/system/site-information.

It is worth reiterating that if you need to read the original configuration values in an override, for example, to compare or merge, you would need to load from the \Drupal::configFactory and not \Drupal::config to avoid a nested loop into your override: -

$original = \Drupal::configFactory()->getEditable('system.site')->getOriginal('name', FALSE);

Even more background information

The overrides system in its latest/current form was added in #2098119: Replace config context system with baked-in locale support and single-event based overrides.

For historical/obsolete information see #1646580: Implement Config Events and Listeners, and storage realms for localized configuration where the configuration override system was originally introduced as well as #1763640: Introduce config context to make original config and different overrides accessible where it was substantially modified to work with contextual access. Language-specific overrides were added later in #2020361: Create LanguageConfigContext to activate configuration overrides based on language.

STILL MISSING FROM THIS DOCUMENTATION

Config overrides + cacheability metadata: #2512718: EntityManager::getTranslationFromContext() should add the content language cache context to the entity+ #2524082: Config overrides should provide cacheability metadatahttps://www.drupal.org/node/2532882

Help improve this page

Page status: No known problems

You can: