Domain Config UI¶
The Domain Config UI module provides a lightweight way to enable per-domain configuration overrides directly from existing configuration forms. When viewing a supported configuration form within a domain context, privileged users will see an inline toggle to enable (or remove) a domain-specific override for that configuration object.
How it works¶
- When a user with the appropriate permissions visits an admin configuration
form on a domain host (e.g.,
https://one.example.com), the module inspects the form to identify the underlying configuration object(s). - If the configuration is allowed to be overridden per domain, an inline
action link appears at the top of the form:
- Enable domain configuration
- Remove domain configuration (after it has been enabled)
- Once enabled, changes submitted on that form are stored as per-domain overrides, without altering the global configuration.
Disallowed configurations¶
You can explicitly prevent certain configuration objects from being overridden per domain. When a configuration name is disallowed, the inline toggle link is hidden on the corresponding form.
Where to configure¶
- UI: Administration > Configuration > Domain > Domain Config UI
(
/admin/config/domain/config-ui) - Config key:
domain_config_ui.settings: disallowed_configurations
Example¶
To forbid domain-level overrides for the Site Information form
(system.site) and the Theme settings (system.theme), set the following
in your configuration (YAML), or via the settings form:
domain_config_ui.settings:
disallowed_configurations:
- system.site
- system.theme
With the above, the "Enable domain configuration" link will no longer appear on:
- Site Information:
/admin/config/system/site-information(system.site) - Appearance and theme settings pages that map to
system.theme
Notes¶
- The module also prevents overriding its own settings by default.
- The check is enforced server-side via the configuration factory, not just hidden in the UI.
Related issue¶
Programmatic control¶
Two alter hooks allow advanced control over where the toggle is shown and which configuration objects are eligible for per-domain overrides.
Disallowed configurations alter¶
Prevent domain overrides for specific configuration names globally:
/**
* Implements hook_domain_config_ui_disallowed_configurations_alter().
*/
function mymodule_domain_config_ui_disallowed_configurations_alter(array &$disallowed): void {
// Disallow domain overrides for image toolkit settings site-wide.
$disallowed[] = 'system.image';
}
Disallowed routes alter¶
Hide the toggle entirely on specific routes (even if the underlying config would normally be allowed):
/**
* Implements hook_domain_config_ui_disallowed_routes_alter().
*/
function mymodule_domain_config_ui_disallowed_routes_alter(array &$routes): void {
// Do not show the toggle on the account settings page.
$routes[] = 'entity.user.admin_form';
}
Config editing domain (developer API)¶
By default the configuration forms read from and write to the negotiated domain — the one resolved from the current request (hostname, path prefix, and so on). That same negotiated domain also drives routing, language negotiation and path-prefix handling, so it must reflect the real request and cannot simply be swapped to edit another domain.
To let code edit another domain's overrides without changing the negotiated
domain, the module provides the DomainConfigEditContext service
(Drupal\domain_config_ui\DomainConfigEditContext). It holds an optional
config-editing domain, scoped to a set of configuration names. The
configuration factory and DomainConfigUIManager consult it in preference to
the negotiated domain, falling back to negotiation when nothing is set.
/** @var \Drupal\domain_config_ui\DomainConfigEditContext $edit_context */
$edit_context = \Drupal::service(DomainConfigEditContext::class);
// Edit one_example_com's system.site overrides only; everything else (and
// routing, language and path prefixes) keeps using the negotiated domain.
$edit_context->setEditingDomain('one_example_com', ['system.site']);
Key properties:
- Name-scoped: only the configuration names you pass are retargeted; any other configuration still resolves to the negotiated domain.
- Negotiation is untouched: routing, language and path-prefix handling always use the real request domain, so the current URL keeps working — including on sites using language path prefixes or path-prefix domain mode.
- Backward compatible: when no editing domain is set, every resolution falls back to the negotiated domain, so behavior is identical to not using the service at all.
- Access still applies: writes remain gated by the per-domain registration and the user's domain permissions, evaluated against the editing domain — a user can only write a domain they may administer.
This API powers the Domain Configuration Switcher submodule in the
domain_extras project, which adds an in-form domain selector.
Editing the base (default) configuration¶
Pass DomainConfigEditContextInterface::BASE as the domain id to edit the
base configuration — the default values every domain inherits unless it
has its own per-domain override — instead of a specific domain's override.
getDomainId() resolves the sentinel to NULL, so the configuration factory
writes the base configuration rather than a domain collection, even when the
negotiated domain has a registered override.
use Drupal\domain_config_ui\DomainConfigEditContextInterface;
// Edit the base system.site configuration, regardless of the negotiated
// domain. Saving writes the default that non-overriding domains inherit.
$edit_context->setEditingDomain(
DomainConfigEditContextInterface::BASE,
['system.site'],
);
Because getDomainId() resolves to NULL in base mode, domain_config_ui's own
hook_form_alter() treats the request as having no editing domain and does not
run: it adds neither the enable/disable toggler nor the permission validators.
The consumer therefore owns the in-form UI and must gate base editing behind the
set default domain configuration permission itself.
Related issue¶
Permissions overview¶
Common permissions used by this module include:
use domain config ui— see and use the inline toggle on allowed formsadminister domain config ui— manage settingsset default domain configuration— manage default vs. domain-specific values
Ensure users operate within a domain context (i.e., visiting the site on a domain's host) for the toggle to be available.
The optional Domain Config Language UI
submodule contributes its own translate domain configuration permission for
managing per-language overrides on top of per-domain ones.
Testing references¶
The repository includes functional and JavaScript tests that illustrate expected behavior:
DomainConfigUISettingsTest— enabling/removing overrides from common formsDomainConfigUIDisallowedConfigurationsTest— verifies that addingsystem.sitetodisallowed_configurationshides the toggle on Site InformationDomainConfigUIOptionsTestandDomainConfigUIPermissionsTest— permissions and options coverage
These tests can serve as examples when integrating the feature in custom modules.
Related submodules¶
Per-domain configuration can affect rendering that depends on
language-keyed plugin definitions cached by Drupal core (local tasks,
local actions, contextual links). Without a cache key that also varies
by domain, the first domain to warm the cache wins, and subsequent
domains render stale tabs and actions. The companion submodule
domain_menu_extras
(in the domain_extras project) replaces the affected plugin managers
with domain-aware variants. Enable it if you're affected.