drupal-config-mgmt
Skill81 starsChanged 42 days agoMITView on GitHub
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.
- Deletes or force-pushes
In this repository25 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- acquia-sourceSkill
- canvas-contributionSkill
- claude-code-mcp-setupSkill
- design-reviewSkill
- drupal-api-endpointsSkill
- drupal-at-your-fingertipsSkill
- drupal-canvas-sdcSkill
- drupal-canvasSkill
- drupal-config-reconcileSkill
- drupal-contrib-mgmtSkill
- drupal-ddevSkill
- drupal-deploy-safetySkill
- drupal-mailSkill
- drupal-performanceSkill
- drupal-search-apiSkill
- drupal-simple-oauthSkill
- drupal-testingSkill
- drupal-views-patternsSkill
- drupal-vite-themeSkill
- ivangrynenko-cursorrules-drupalSkill
- mockup-paritySkill
- sentry-integrationSkill
- skill-developerSkill
The file
We split this skill into the rules it gives an agent
12 rules. About 0 are common, 8 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈8
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 102
Best Practice: Always preview changes first:
- Distinctive: line 114
⚠️ IMPORTANT: When updating config split definitions, changes must be in ACTIVE configuration (database), not just exported files!
- Very specific: line 205
Use config:get and config:status for read-only inspection, or use --no flag with cim/cex to prevent auto-confirmation.
- Very specific: line 231
CRITICAL: Always use --no flag with terminus!
- Distinctive: line 302
Check what config would be imported (read-only):
- Very specific: line 314
Always inspect before importing: - Use config:get and --no --diff flags
- Distinctive: line 318
Clean up temp files: - Remove temporary YAML files
- Distinctive: line 319
Verify before committing: - Always review git diff output
- Distinctive: line 320
Test locally first: - Import and test before deploying
- Distinctive: line 321
Use config splits: - Keep environment-specific config separate
- Distinctive: line 329
If files are marked as deleted in git status:
- Very specific: line 388
Config entity UUID differs: not an error, but the import deletes and recreates that entity (for a field storage, its data goes with it).
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
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:statusTable 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:exportExport 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.ymlImporting 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 onlyImport 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 valueBest Practice: Always preview changes first:
ddev drush config:import --no --diff # Show what would change
ddev drush cim --no --diff # AliasConfig 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:exportThis 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 cimThis 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.phpA --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 cssExample output:
Split Active Configuration directory
local Yes ../config/local
dev No ../config/dev
test No ../config/testSafe 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.ymlCRITICAL: 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 cimVia 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:statusBest 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/*.ymlThis can happen if a drush command runs unexpectedly.
Split not activating
Check split status:
ddev drush config-split:statusManually 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}.ymlConfig 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.ymlSolution: 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 existsSee 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
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

