drupal-config-mgmt
Guides Drupal configuration management safely - single-config export/set/delete, config:import and config:export (cim/cex) with preview-first --no --diff, config:status checks, Config Split (complete vs partial splits, csex/csim, activation), and syncing config from remote environments through Terminus, acli, platform/upsun, lagoon or drush aliases. Use when exporting or importing Drupal config, inspecting or changing a single config object, merging production config into a feature branch, working with config_split, or diagnosing config that will not import or vanishes from config/default on export.
How do I install this agent skill?
npx skills add https://github.com/grasmash/drupal-claude-skills --skill drupal-config-mgmtIs this agent skill safe to install?
- Gen Agent Trust Hubpass
This skill provides comprehensive instructions for Drupal configuration management using industry-standard tools like Drush, Git, and various hosting CLIs. While it executes shell commands and uses dynamic PHP evaluation to update configuration, these actions are essential for its purpose and are implemented using established developer workflows.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
- Runlayerwarn
2/2 files flagged
- ZeroLeakspass
Score: 93/100 · 2 sections analyzed
What does this agent skill do?
Drupal Configuration Management
Comprehensive guide for Drupal configuration management including imports, exports, config splits, and environment syncing.
Remote CLI — host-neutral
Examples below use Pantheon's Terminus (terminus drush <site>.<env> -- <cmd>). The commands are identical on any host — substitute your platform's remote-drush form. If your platform provides Drush site aliases, the generic drush @<alias> <cmd> works everywhere.
| Task | Acquia (acli) | Pantheon (Terminus) | Platform.sh / Upsun | Lagoon (amazee.io) | Generic (Drush aliases) |
|---|---|---|---|---|---|
| Remote drush | acli remote:drush -- <cmd> | terminus drush <site>.<env> -- <cmd> | platform drush -e <env> -- <cmd> (Upsun: upsun drush …) | lagoon ssh -p <project> -e <env> -C "drush <cmd>" | drush @<alias> <cmd> |
| Get/inspect config | acli remote:drush -- config:get <name> | terminus drush <site>.<env> -- config:get <name> | platform drush -e <env> -- config:get <name> | lagoon ssh -p <project> -e <env> -C "drush config:get <name>" | drush @<alias> config:get <name> |
The auto-confirm warning below applies to every host — append
--notocim/config:importto preview instead of apply. Drush defaults to--yeswhen invoked non-interactively (which all remote-CLI wrappers do).
Problem: Avoid Accidental Config Imports
CRITICAL: Terminus drush commands default to --yes unless explicitly told --no. This means commands like config:import or cim will AUTO-CONFIRM and import configuration even when you only want to inspect differences.
Dangerous vs Safe Patterns
❌ DANGEROUS - Auto-imports without confirmation:
terminus drush {site}.{env} -- cim --diff # DON'T DO THIS!
✅ SAFE - Shows diff without importing:
terminus drush {site}.{env} -- cim --no --diff
✅ SAFEST - Read-only commands:
terminus drush {site}.{env} -- config:get config.name
terminus drush {site}.{env} -- config:status
Table of Contents
- Preferred Prod Config Merge Workflow
- Configuration Import & Export Basics
- Config Splits Overview
- Complete vs Partial Splits
- Config Split Commands
- Safe Inspection Workflow
- Syncing Config from Upstream Environments
Configuration Import & Export Basics
Default to one named object at a time. A blanket
cexwrites every active-vs-sync difference to disk and a blanketcimimports (and deletes) every difference, so in a tree with drift you did not create they sweep that drift into your commit or your database. An agent changes config withconfig:get/config:set/config:delete/ a partial import of one file, and leaves the full import to the deploy (the tail is in thedrupal-deploy-safetyskill). Run a blanket export only from a clean, committed tree when a full export is the point (a prod config merge, a split change), and reviewgit status config/afterwards. Full rules: surgical-config.md.
Exporting Configuration
Export ALL configuration (from active config to YAML files; clean tree only, see above):
# Local
ddev drush config:export
ddev drush cex
# Remote
terminus drush {site}.{env} -- config:export
Export a SINGLE config object:
# Get config and save to file
ddev drush config:get config.name --format=yaml > config/default/config.name.yml
# Example: Export a specific view
ddev drush config:get views.view.content --format=yaml > config/default/views.view.content.yml
Importing Configuration
Import ALL configuration (from YAML files to active config; this is what a deploy does, so preview locally and leave remote imports to the deploy tail):
# Local
ddev drush config:import
ddev drush cim
# Remote (DANGEROUS - auto-confirms with terminus!)
terminus drush {site}.{env} -- config:import --no # Use --no to preview only
Import a SINGLE config object (partial import from a directory holding only that file; under DDEV the directory must be inside the project, see surgical-config.md):
mkdir -p .config-one && cp config/default/config.name.yml .config-one/
ddev drush config:import --partial --source=/var/www/html/.config-one -y
rm -rf .config-one
# Or use config:set for specific values
ddev drush config:set config.name key.subkey value
Best Practice: Always preview changes first:
ddev drush config:import --no --diff # Show what would change
ddev drush cim --no --diff # Alias
Config Split Commands
CRITICAL: Active vs Exported Configuration
⚠️ IMPORTANT: When updating config split definitions, changes must be in ACTIVE configuration (database), not just exported files!
Workflow:
- Edit
config/default/config_split.config_split.{name}.yml - Import to make active: a single-file partial import (see above) OR use PHP (see below)
- Export:
ddev drush cex. This is the one routine case that needs a full export, because config_split writes the split directory and its patch files duringcex. Start from a clean tree and revert anything ingit status config/you did not intend.
Quick method - Set active config via PHP:
ddev drush php:eval "\$config = \Drupal::configFactory()->getEditable('config_split.config_split.local'); \$config->set('partial_list', ['config.name']); \$config->save();"
See examples.md for detailed workflow.
Export Config with Splits
Export ALL config including active splits:
ddev drush config:export
This exports:
- Base config to
config/default/ - Active split config to
config/{split-name}/
Export a specific split:
ddev drush config-split:export {split-name}
ddev drush csex {split-name}
Import Config with Splits
Import ALL config including active splits:
ddev drush config:import
ddev drush cim
This imports:
- Base config from
config/default/ - Active split config from
config/{split-name}/
Import a specific split:
ddev drush config-split:import {split-name}
ddev drush csim {split-name}
Import only base config (ignore splits): Drush 13's config:import has no option for this (--skip-modules was Drush 8). drush config-split:deactivate <split> itself imports the config without the split, writing status: false into active config; a later full config:import re-activates the split if its synced YAML has status: true. To keep it off across imports, set a status override: drush config-split:status-override <split> inactive (stored in state; values active|inactive|default, alias csso), or in settings.php:
ddev drush config-split:deactivate {split-name} # one-off
ddev drush config-split:status-override {split-name} inactive # sticks across imports
$config['config_split.config_split.{split-name}']['status'] = FALSE; // settings.php
A --partial import skips config transformation, so no split is applied to it at all.
Activate/Deactivate Splits
Activate a split:
ddev drush config-split:activate {split-name}
Deactivate a split:
ddev drush config-split:deactivate {split-name}
Check Split Status
List all splits and their status:
ddev drush config-split:status
ddev drush css
Example output:
Split Active Configuration directory
local Yes ../config/local
dev No ../config/dev
test No ../config/test
Safe Inspection Workflow
Use config:get and config:status for read-only inspection, or use --no flag with cim/cex to prevent auto-confirmation.
Get Config Values
# Get full config object
terminus drush {site}.{env} -- config:get config.name
# Get as YAML
terminus drush {site}.{env} -- config:get config.name --format=yaml
# Extract specific values
terminus drush {site}.{env} -- config:get config.name 2>&1 | grep "setting_name"
Compare Local vs Remote
# View diffs without importing (SAFE with --no)
terminus drush {site}.{env} -- cim --no --diff
# Get remote and compare manually
terminus drush {site}.{env} -- config:get config.name --format=yaml > /tmp/remote.yml
diff -u config/default/config.name.yml /tmp/remote.yml
CRITICAL: Always use --no flag with terminus! Without it, commands auto-confirm.
Apply Changes
Preferred: never hand-author the YAML. Either make the change in the site (UI, config:set for simple config, or the entity API for config entities) and export that one object, or, to take an environment's value, write that environment's config:get output for the one object (see surgical-config.md):
ddev drush config:get config.name --format=yaml > config/default/config.name.yml
# or: terminus drush {site}.{env} -- config:get config.name --format=yaml > config/default/config.name.yml
git diff config/default/config.name.yml
git add config/default/config.name.yml
git commit -m "Update config from {env}"
Syncing Config from Upstream Environments
Quick Methods
Single config object:
terminus drush {site}.{env} -- config:get config.name --format=yaml > config/default/config.name.yml
git add config/default/config.name.yml && git commit -m "Update from {env}"
# Then apply that one file locally with a single-file partial import (see above)
Full config sync via rsync:
Caution: a remote
cexwrites every drifted config object on that environment, and a localcimimports (and deletes) everything that differs. Never run a blanket export/import against a shared or remote environment as a casual step — prefer the single-config method above, and review the full diff before anything is imported. See surgical-config.md.
terminus drush {site}.{env} -- cex
terminus rsync {site}.{env}:code/config/default /tmp/remote
diff -r config/default /tmp/remote # Review
cp /tmp/remote/*.yml config/default/
git add config/default/ && git commit -m "Sync from {env}"
ddev drush cim
Via database pull (DDEV + Pantheon). This is a deliberate full export: commit your own work first and restore any of your files the export deletes, as in prod-config-merge.md:
ddev pull pantheon --environment={env} # Warning: Overwrites local DB!
ddev drush cex
git diff config/ && git add config/ && git commit -m "Config from {env}"
See examples.md for detailed workflows.
Best practices: Review diffs, commit separately, test locally, document source, avoid syncing environment-specific config.
Deep Dive References
For comprehensive technical documentation, see:
- config-split-deep-dive.md - Complete technical reference on Config Split 2.0, patch files, export/import process, and dependency handling
- surgical-config.md - One-config-at-a-time export/set/delete for agents, the
core.extension.ymlexception, raw config writes that dropdependencies, baked (PHP-computed) config, and verifying imports withconfig:status - examples.md - Practical examples and workflows
References
| File | Read it when |
|---|---|
| references/surgical-config.md | Before any config change an agent makes: exporting, setting or deleting ONE named config object, the core.extension.yml exception, raw config writes that drop dependencies, baked (PHP-computed) config, verifying an import with config:status, shipping a config-only change |
| references/prod-config-merge.md | Merging production config changes into a branch that carries local feature config (pull prod DB, export, restore your deleted/overwritten files) |
| references/config-splits.md | Deciding whether to use a split, or choosing between a Complete and a Partial split |
| references/config-split-deep-dive.md | You need the Config Split 2.0 internals: patch files, file naming, the export/import process, dependency handling, and worked local-vs-remote Solr examples (including a complete-split server with auto-generated index patches) |
| references/examples.md | You want worked examples: syncing one config from dev, syncing Search API config, comparing environments, updating split definitions |
Config Status Check
Check what config would be imported (read-only):
# Local environment
ddev drush config:status
# Remote environment
terminus drush {site}.{env} -- config:status
Best Practices
- Always inspect before importing - Use
config:getand--no --diffflags - Change config in the site, export one object - UI /
config:set/ entity API, thenconfig:get --format=yaml; hand-editing exported YAML is the exception (it skips dependency calculation), and when you do it, import that one file and confirm withconfig:status - One config type per commit - Separate concerns for clean history
- Clear commit messages - Reference source environment
- Clean up temp files - Remove temporary YAML files
- Verify before committing - Always review
git diffoutput - Test locally first - Import and test before deploying
- Use config splits - Keep environment-specific config separate
Troubleshooting
Config files deleted from working directory
If files are marked as deleted in git status:
git checkout HEAD -- config/default/*.yml
This can happen if a drush command runs unexpectedly.
Split not activating
Check split status:
ddev drush config-split:status
Manually activate:
ddev drush config-split:activate {split-name}
# Save the activation state: export only the split definition
ddev drush config:get config_split.config_split.{split-name} --format=yaml > config/default/config_split.config_split.{split-name}.yml
Config deleted from config/default on export
COMMON ISSUE: Config (like search_api.server.my_search_server) gets removed from config/default/ when you run drush cex.
Root cause (99% of cases): Config is in complete_list instead of partial_list!
Complete split = Config is REMOVED from config/default/ and moved to split directory entirely
Partial split = Config STAYS in config/default/, only differences are patched
Diagnosis:
# Check if config is in complete_list (will be deleted from config/default)
grep -A10 "complete_list:" config/default/config_split.config_split.local.yml
# Check if config is in partial_list (will stay in config/default)
grep -A10 "partial_list:" config/default/config_split.config_split.local.yml
Solution: Move from complete_list to partial_list
# Edit the split definition
# Move: search_api.server.my_search_server
# FROM: complete_list
# TO: partial_list
ddev drush cex # Re-export
# Check that config/default/search_api.server.my_search_server.yml exists
# Check that config/local/config_split.patch.search_api.server.my_search_server.yml exists
See config-split-deep-dive.md for complete technical explanation.
Config won't import
Common issues:
- Dependencies missing: Install required modules first
- Site UUID mismatch ("Site UUID in source storage does not match the target storage"): the database was installed separately from the exported config. Install from config with
drush site:install --existing-config, or setsystem.site:uuidto the exported value withdrush config:set system.site uuid <uuid> - Config entity UUID differs: not an error, but the import deletes and recreates that entity (for a field storage, its data goes with it); see the
drupal-config-reconcileskill
Drush 13's config:import has no --skip-config option. To keep one object out of an import, use the config_ignore module or a single-file partial import (above).
Related Commands
Read-only: config:get, config:status
Exports: config:export (alias: cex)
Imports: config:import (alias: cim) - Use with --no --diff to preview
Splits: config-split:status, csex, csim, config-split:activate
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/grasmash/drupal-claude-skills/drupal-config-mgmt">View drupal-config-mgmt on skillZs</a>