| name | drupal-config-management |
| description | Configuration management patterns for Drupal 10/11 — config split, config ignore, environments, import/export workflows, config readonly, and the sync/install/optional directory hierarchy. Use when working with config export/import, multi-environment deployments, config split setup, or managing configuration across dev/staging/prod. |
Drupal Config Management
Configuration is Drupal's declarative system for site building. It lives in YAML files and is the bridge between development and deployment. Getting config management right is the difference between smooth deploys and broken production sites.
Config Directories
Drupal supports multiple config directories. Understanding which one to use is critical.
| Directory | Purpose | When Populated |
|---|
config/sync | Primary sync directory for drush cim/drush cex | drush cex writes here |
config/default | Legacy name for sync (Drupal 8 projects) | Same as sync |
config/staging | Alternative name used by some hosting platforms | Same as sync |
modules/custom/*/config/install | Default config installed when module is enabled | Module install |
modules/custom/*/config/optional | Config installed only if dependencies are met | Module install |
install vs optional
- install/: Config that MUST exist when the module is enabled. If it fails to install, the module enable fails.
- optional/: Config that is installed IF the required dependencies (modules, entity types) exist. Silently skipped if dependencies are missing.
Import and Export
drush cex -y
drush cim -y
drush cim --partial --source=modules/custom/my_module/config/install -y
drush config:status
drush config:get system.site name
drush config:set system.site name "My Site" -y
Config Split
Config Split (config_split module) is the standard tool for managing environment-specific configuration. It allows different config per environment (dev, staging, prod) from a single codebase.
How it works
Config Split defines "splits" — named sets of config that can be:
- Complete split: Config exists ONLY in the split, not in sync. Used for dev-only modules.
- Conditional split: Config exists in sync with default values, but the split overrides specific values. Used for environment-specific settings.
Setup
id: dev
label: Development
folder: ../config/split/dev
status: true
module:
devel: 0
stage_file_proxy: 0
complete_list:
system.logging: {}
conditional_list: {}
Activation per environment
In each environment's settings.php or settings.local.php:
$config['config_split.config_split.dev']['status'] = TRUE;
$config['config_split.config_split.prod']['status'] = FALSE;
$config['config_split.config_split.dev']['status'] = FALSE;
$config['config_split.config_split.prod']['status'] = TRUE;
Split directory structure
config/
sync/ # Base config (shared across all environments)
split/
dev/ # Dev-only config (devel, verbose logging)
staging/ # Staging-specific overrides
prod/ # Prod-specific config (CDN, caching)
Workflow
drush cex -y
drush cim -y
Config Ignore
config_ignore module prevents specific config from being overwritten during import. Useful for site-specific settings that should never be deployed from code.
ignored_config_entities:
- system.site
- core.extension~module.devel
- webform.webform.*
When to use config_ignore vs config_split:
- Config split: Different values per environment (dev has devel enabled, prod doesn't).
- Config ignore: Config that should never be deployed from code (site name, contact form recipients set by site admins).
Config Readonly
config_readonly module prevents config changes via the admin UI in production. All config changes must go through code deployment.
$settings['config_readonly'] = TRUE;
$settings['config_readonly_whitelist_patterns'] = [
'system.site',
'contact.form.*',
];
Config Schema
Every config file should have a schema definition. Without it, drush cim may silently accept invalid config.
my_module.settings:
type: config_object
label: 'My Module settings'
mapping:
enabled:
type: boolean
label: 'Enabled'
max_items:
type: integer
label: 'Maximum items'
api_endpoint:
type: string
label: 'API endpoint URL'
Config Translation
For multilingual sites, config translation stores translated strings separately:
label: 'Οι ρυθμίσεις μου'
description: 'Ρυθμίσεις του module'
Config translation files live in config/sync/language/LANGCODE/ and are imported/exported with regular config.
Common Pitfalls
1. Editing config on production without config_readonly.
Fix: Enable config_readonly on production. All changes via code.
2. Conflicting UUIDs when sharing config across sites.
Fix: Never copy config/sync between different Drupal installations. Use config/install in modules for portable config.
3. Missing config dependencies.
Fix: Add dependencies.module and dependencies.config to your config files. Without these, drush cim may import config before its dependencies exist.
4. Config not importing because of config_ignore.
Fix: Check config_ignore.settings for patterns that match your config.
5. Forgetting to export after admin UI changes.
Fix: Run drush config:status before committing. If it shows changes, run drush cex -y.
Workflow Checklist
- Make config changes in dev (UI or code).
- Export:
drush cex -y.
- Review the diff:
git diff config/.
- Commit config changes with the code that uses them.
- On staging:
drush cim -y && drush cr.
- On prod:
drush cim -y && drush cr (with config_readonly re-enabled after).