From drupal-workflow
Manages Drupal 10/11 config with splits, ignores, environments, import/export workflows, readonly mode, and sync/install/optional hierarchies for multi-env deployments.
npx claudepluginhub gkastanis/drupal-workflow --plugin drupal-workflowThis skill uses the workspace's default tool permissions.
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.
Provides guides and best practices for managing app configuration: environment variables, hierarchies, secrets, feature flags, validation, and 12-factor principles. Use for multi-env setups.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
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.
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 |
# config/install/my_module.settings.yml
# Always installed with the module.
# config/optional/views.view.my_module_listing.yml
# Only installed if the Views module is enabled.
# Export all active config to sync directory.
drush cex -y
# Import config from sync directory to active.
drush cim -y
# Import only specific config (partial import).
drush cim --partial --source=modules/custom/my_module/config/install -y
# Show differences between active and sync.
drush config:status
# Get a specific config value.
drush config:get system.site name
# Set a specific config value (use sparingly — prefer YAML files).
drush config:set system.site name "My Site" -y
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.
Config Split defines "splits" — named sets of config that can be:
# config/sync/config_split.config_split.dev.yml
id: dev
label: Development
folder: ../config/split/dev
status: true # Active on dev, inactive elsewhere.
module:
devel: 0 # Enable devel module on dev only.
stage_file_proxy: 0 # Enable stage_file_proxy on dev only.
complete_list:
system.logging: {} # Verbose logging only on dev.
conditional_list: {}
In each environment's settings.php or settings.local.php:
// settings.local.php (dev)
$config['config_split.config_split.dev']['status'] = TRUE;
$config['config_split.config_split.prod']['status'] = FALSE;
// settings.php (prod)
$config['config_split.config_split.dev']['status'] = FALSE;
$config['config_split.config_split.prod']['status'] = TRUE;
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)
# On dev: export with splits active.
drush cex -y
# Both config/sync/ and config/split/dev/ are updated.
# On staging/prod: import with that environment's split active.
drush cim -y
# Config from config/sync/ + config/split/prod/ is imported.
config_ignore module prevents specific config from being overwritten during import. Useful for site-specific settings that should never be deployed from code.
# config/sync/config_ignore.settings.yml
ignored_config_entities:
- system.site # Site name/slogan set per environment.
- core.extension~module.devel # Ignore devel module status.
- webform.webform.* # Ignore all webform submissions config.
When to use config_ignore vs config_split:
config_readonly module prevents config changes via the admin UI in production. All config changes must go through code deployment.
// settings.php (production only)
$settings['config_readonly'] = TRUE;
// Allow specific forms to bypass readonly (e.g., site settings).
$settings['config_readonly_whitelist_patterns'] = [
'system.site',
'contact.form.*',
];
Every config file should have a schema definition. Without it, drush cim may silently accept invalid config.
# config/schema/my_module.schema.yml
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'
For multilingual sites, config translation stores translated strings separately:
# language/el/my_module.settings.yml (Greek translations)
label: 'Οι ρυθμίσεις μου'
description: 'Ρυθμίσεις του module'
Config translation files live in config/sync/language/LANGCODE/ and are imported/exported with regular config.
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.
drush cex -y.git diff config/.drush cim -y && drush cr.drush cim -y && drush cr (with config_readonly re-enabled after).