Post

Drush config:import cheatsheet — managing Drupal config across environments

What is drush config:import?

Drupal stores site configuration — think field settings, view modes, content types, display modes, roles, permissions, module settings — as YAML files on disk. drush config:import (alias drush cim) reads those YAML files and applies them to the active database configuration.

This is the opposite of drush config:export (drush cex), which dumps the active database config out to YAML files.


The canonical workflow

Work locally, commit config to Git, let CI/CD deploy it to remote environments.

1
2
3
4
5
6
Local (DDEV)             Git                    Remote (staging/prod)
────────────             ───                    ─────────────────────
Make UI changes    →     commit config/sync  →  CI/CD runs on every deploy:
drush cex                (or config/feature)      git pull
                                                  drush config:import -y
                                                  drush cache:rebuild
  • config/sync/ — the default. Use this for all regular config work. Full site config, versioned, deployed on every merge.
  • config/feature/ — use when you need to deploy a specific subset independently, e.g. a hotfix to one view without pulling in other unfinished config from config/sync/.

Always run drush config:import on every deploy — even if no config changed. It’s idempotent: if the YAMLs haven’t changed, it’s a no-op. Safer than trying to conditionally skip it.


The benefits of config management

BenefitWhy it matters
Version controlled configSee exactly what changed, when, and who changed it — same as code
Multi-environment deploymentsExport on local, commit, import on staging/prod — no manual clicking through the UI
Code review for configConfig changes go through PRs just like code changes
Disaster recoveryRecreate any environment from scratch using code + config
Feature flags / partial deploysShip just the config for one feature without touching everything else

Start simple — the default config sync

The most common usage is a full sync using the default config/sync/ directory. This is where Drupal stores all your site config by default.

Export (local → files)

1
2
3
4
5
# Inside DDEV
ddev drush config:export

# Outside DDEV
drush config:export

This writes all active database config to config/sync/ as YAML files. Commit these to Git.

Import (files → database)

1
2
3
4
5
# Inside DDEV
ddev drush config:import

# Outside DDEV
drush config:import

This reads the YAML files in config/sync/ and syncs them into the active database. A full import will delete any config in the DB that isn’t present in the sync directory — it’s a true sync, not a merge.

Always rebuild cache after importing:

1
drush cache:rebuild

Feature config — partial imports

Once you’re comfortable with the default workflow, partial imports let you deploy just a subset of config — useful when you have a feature directory or only want to push specific changes without a full sync.

1
drush config:import --partial --source=/var/www/html/config/feature
FlagWhat it does
--partialOnly import the YAMLs present in the source directory — don’t delete configs that exist in the DB but are absent from the source
--source=PATHRead YAMLs from this directory instead of the default sync directory

Why --partial matters

Without --partial, a full sync would delete any config in the DB not present in the source directory. When you’re only importing a feature subdirectory, that would wipe out all your other config. --partial makes it additive/update-only.

How to build a feature config directory

1
2
3
4
5
6
# Option 1 — export directly to a feature directory
drush config:export --destination=/var/www/html/config/feature

# Option 2 — manually copy specific YAMLs from your full sync
cp config/sync/views.view.my_view.yml config/feature/
cp config/sync/field.field.node.article.body.yml config/feature/

Then commit and import the same way:

1
2
3
4
5
6
git add config/feature/
git commit -m "Export view config for my-feature"

# On target environment
drush config:import --partial --source=/var/www/html/config/feature
drush cache:rebuild

Ideal workflows

Solo developer

You’re the only one touching config so conflicts are rare. The main discipline is: always export before you commit code that depends on config changes. The trap is writing a module or template that relies on a field or view you set up in the UI, then forgetting to export — it breaks on the next environment.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. Make config changes in the Drupal UI
# 2. Export
ddev drush config:export

# 3. Review the diff — only commit YAMLs you intentionally changed
git diff config/sync/

# 4. Commit and push
git add config/sync/
git commit -m "Export config: updated news view"
git push

# 5. On staging/prod
git pull
drush config:import
drush cache:rebuild

Team workflow

This is where it gets more nuanced — two developers can both change config locally, creating conflicts in YAML files. Treat config exactly like code: export it, review it, merge it.

Golden rule: always sync before you start work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. Before starting work — get your local DB in sync with the team
git pull
ddev drush config:import

# 2. Do your UI changes locally

# 3. Export and review the diff
ddev drush config:export
git diff config/sync/   # only commit what you intentionally changed

# 4. Commit in its own commit, then raise a PR
git add config/sync/
git commit -m "Config: add body field to article content type"

# 5. Teammate reviews the YAML diff in the PR (same as reviewing code)

# 6. On merge, staging/prod deploy runs:
drush config:import -y
drush cache:rebuild

Handling YAML merge conflicts — When two devs both export different config states, Git will flag conflicts in the YAML files. They’re usually readable and can be resolved manually the same way you’d resolve a code conflict. Keep config exports small and focused to minimise conflicts.

ConventionWhy
Pull + import before starting any config workAvoids diverging from the team’s current state
Config changes in their own commitKeeps diffs clean and reviewable
One person owns config export per featureAvoid two devs exporting different states simultaneously
--partial for feature-level work onlyUse full cim for main branch deploys
Never import on production without a DB backupdrush sql:dump first, always
CI/CD runs drush config:import on every deployAutomates it, removes human error

CI/CD deploy script

1
2
3
4
5
git pull
composer install --no-dev
drush updatedb -y        # run DB updates from module upgrades
drush config:import -y   # apply config changes
drush cache:rebuild

Multi-environment flow at a glance

1
2
Local dev  →  Git commit config  →  Staging  →  Production
   cex              push               cim          cim

When do you need to run it?

Run drush config:import when:

  • Deploying code that includes config changes (views, fields, content types, permissions, etc.)
  • A teammate has updated config on their local and pushed it to Git
  • After enabling or updating a module that ships with config
  • After a git pull that includes YAML file changes — if you don’t run it, the DB and files are out of sync

Always run drush cache:rebuild (drush cr) after importing config.


Safe usage checklist

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. Check what will change BEFORE applying it
drush config:import --preview
# or
drush config:import --diff

# 2. Backup the database first (especially on staging/prod)
drush sql:dump --result-file=/tmp/backup-before-cim.sql

# 3. Run the import
drush config:import

# 4. Rebuild cache
drush cache:rebuild

# 5. Verify in the UI

Common flags

FlagPurpose
--partialOnly import files in source dir, don’t delete unmatched configs
--source=PATHImport from a custom directory instead of the default sync dir
--previewShow a diff of what will change without applying it
--diffSame as preview but outputs as a unified diff
-yAuto-confirm without prompting (useful in CI/CD pipelines)

Common gotchas

Config UUID mismatch — If you import config from a completely different site install, Drupal will refuse. Each Drupal install has a unique UUID stored in system.site.yml. You can override it:

1
drush config:set system.site uuid "the-uuid-from-your-source-site"

Config out of sync after module enable — After enabling a module, always import config if the module ships config YAML files in its config/install/ directory.

Partial import missing dependencies — If a YAML file references a config entity that doesn’t exist yet (e.g. a field that hasn’t been created), the import will fail. Import dependencies first or do a full config import.


Quick reference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Export full config
drush config:export                           # drush cex

# Import full config
drush config:import                           # drush cim

# Preview changes without applying
drush config:import --preview

# Backup DB then import
drush sql:dump --result-file=/tmp/pre-cim.sql && drush config:import

# Import partial (feature/subset only)
drush config:import --partial --source=config/feature

# Inside DDEV
ddev drush config:import --partial --source=/var/www/html/config/feature
This post is licensed under CC BY 4.0 by the author.