Sorry, you need to enable JavaScript to visit this website.
Skip to main content
Welcome to our website! Explore our services and portfolio.

A Gentle Introduction to Configuration Management in Drupal

Submitted by admin on

Drupal's Config API is the standardized system for storing, retrieving, and synchronizing site configuration data (like views, fields, content types, and module settings) as structured YAML-serialized objects, enabling "configuration as code" across environments.drupalize+1

It distinguishes deployable, version-controlled config from transient data (like State API for cron timestamps), ensuring consistency via export/import workflows.linkedin+1

Core PHP Classes

  • Drupal\Core\Config\ConfigFactoryInterface (and ConfigFactory): Central service factory; use \Drupal::config('module.settings') for read-only ImmutableConfig, or \Drupal::service('config.factory')->getEditable('module.settings') for mutable Config objects to set/save values.jpstacey+2

  • Drupal\Core\Config\Config / ImmutableConfig: Represents a single config object (e.g., system.site.yml); handles get/set/clear operations with dot notation for nested data.antistatique+1

  • Drupal\Core\Config\ConfigEntityBase / ConfigEntityInterface: Base for config entities like nodes, views (e.g., views.view.frontpage); extends entities with exportable storage.[api.drupal]​

  • Drupal\Core\Config\StorageInterface (ActiveStorage, FileStorage): Bridges DB and YAML; config.get() reads active storage, drush cex writes to file storage.[drupalize]​

These classes power \Drupal::config(), form storage, and sync commands like drush cim/cex.drupal+1

MySQL Tables

All config lives in one table: config (simple config + serialized entities).

Column

Purpose

name

Unique config name (PK), e.g., views.view.frontpage, system.site.[drupalize]​

data

BLOB of YAML-parsed PHP array (nested settings, weights, plugins).[drupalize]​

  • No separate tables per config type—everything serialized for portability.[api.drupal]​

  • Active config = rows in config; export = YAML files from these rows.[drupalize]​

  • Schema validation via config.schema.yml files (type safety, defaults).[drupalize]​

Usage Summary

The API ensures config is deployable (export to YAML via drush cex, commit to Git, import via cim), typed (schemas prevent bad data), and overrideable (splits, overrides). In your Acquia/DDEV flow, it's the engine behind cim in hooks and cst checks to block drift—DB rows get overwritten from Git YAML on every deploy.drupal+2

 

 






 

On Acquia Cloud Next, use drush cex to export active DB config to ../config/default/ (Acquia's VCS dir) and drush cim to import from there to DB.acquia+2

Always use Drush site aliases (@mysite.dev) for remote execution over SSH, as direct server access is via Cloud Next SSH.acquia+1

Export Config (drush cex)

Export from the authoritative environment (usually dev/DDEV) after UI changes:

text
# Local/DDEV ddev drush cex -y # Remote Acquia (via alias) drush @mysite.dev cex -y
  • Writes config/default/*.yml from DB config table rows.drush+1

  • -y skips prompts; add --preview first to review diffs.[drush]​

  • Then commit/push: git add config/default/ && git commit -m "Views tweak" && git push.acquia+1

Acquia expects ../config/default/ (not Drupal's default ../config/sync/) for Git-based pipelines.[docs.acquia]​

Import Config (drush cim)

Import after code deploy (manual SSH or pipeline hooks) to sync Git YAML → DB:

text
# Remote Acquia drush @mysite.dev cim -y # With status check first drush @mysite.dev cst # Shows diffs drush @mysite.dev cim -y
  • Overwrites DB config table with YAML from ../config/default/.acquia+1

  • Run in post-code deploy hook (docroot/hooks/deploy/post-code.sh):

    bash
    #!/bin/bash drush cim -y --uri=default drush cr
  • -y automates; use --partial if you want to skip conflicts (rare).[drush]​

Acquia-Specific Workflow

text
1. DDEV/UI change → ddev drush cex → git commit config/default/ → PR 2. CI tests → merge → git push (triggers Acquia deploy) 3. Acquia deploy: code lands → post-code hook runs drush cim → cache rebuild 4. Verify: drush @mysite.prod cst (should be clean)

Status check (drush cst) is your drift detector—run before/after every cim.[docs.acquia]​

Pro tip: With Config Split (from earlier), cex/cim automatically includes the activated split for that env via settings.php. No extra flags needed.mikemadison+1
















 

Best practices for Drupal config sync on Acquia Cloud Next center on treating Git YAML (../config/default/) as the single source of truth, exporting only from dev/DDEV, and automating imports via deploy hooks.acquia+1

Use Config Split + settings.php for env differences and strict CI checks to block drift.acquia+1

Single Authoritative Environment

Always make config changes only on dev/DDEV (never stage/prod), then drush cex → git commit → deploy.specbee+2

  • Prod/stage get read-only config from Git via cim in hooks.[docs.acquia]​

  • One-way flow: dev → git → all other envs.[specbee]​

Git Directory Setup

Acquia standard: ../config/default/ (set in settings.php):

php
$settings['config_sync_directory'] = '../config/default';

Commit entire directory—every YAML file is code.acquia+1

Workflow Steps

  1. Dev change (views, webforms): UI → drush cex -ygit commit config/default/ "Updated view XYZ" → PR.[docs.acquia]​

  2. CI validates: drush cst (no diffs), cim, tests (Behat), cex (git clean). Fail PRs with uncommitted config.[acquia]​

  3. Deploy: Git push → Acquia runs post-code hook drush cim -y; drush cr.acquia+1

  4. Verify: drush @mysite.prod cst shows clean (no drift).[docs.acquia]​

Essential Modules + Patterns

Module

Purpose

Acquia Config

Config Split

Dev/devel modules, logging per env

../config/split/{local,dev,prod} acquia+1

Config Ignore

Site name, local blocks (minimal use)

system.site, block.block.* [acquia]​

Acquia Purge

Auto-purge Varnish after cim

Enabled prod/stage [docs.acquia]​

settings.php auto-activates splits (from earlier example).[docs.acquia]​

Deploy Hook Template

docroot/hooks/deploy/post-code.sh (chmod +x):

bash
#!/bin/bash drush cim -y --uri=default drush cr drush purge:invalidate everything # Varnish [web:26]

Drift Prevention Checklist

  • Never cim manually on prod except emergencies (then cex + commit).[docs.acquia]​

  • Block UI config changes on prod/stage (permissions or workflow).[acquia]​

  • Pre-deploy: drush cst on target shows clean.[docs.acquia]​

  • Post-deploy: Same check + smoke tests.[acquia]​

  • Nightly audit: Script runs cst on prod, Slack alerts on diffs.[configu]​

  • DB syncs (dev←prod): Always cim after to restore Git config.[docs.acquia]​

Module/Core Updates

Critical gotcha—always cex after composer update:

text
1. composer update drupal/module 2. drush updb -y 3. drush cex -y # Capture schema changes 4. git commit

Missing step 3 = broken cim on next deploy.[docs.acquia]​

Your Setup (DDEV → Cloud Next)

text
DDEV (local) → drush cex → git/PR → Acquia dev → stage → prod ↓ post-code: cim + varnish purge

Prod stays pristine because nothing touches its DB config except automated Git→DB imports. Matches your Behat/Varnish workflow perfectly.History+1



























 

Common pitfalls in Drupal config management across Acquia environments include forgetting to export after changes, making direct edits on prod/stage, and mishandling env-specific differences.acquia+1

These lead to broken deploys, overwritten work, or drift where prod diverges silently from Git.

Forgot to Export (Most Common)

Pitfall: UI change on dev → deploy code → cim fails because config/default/ lacks the new YAML.

Fix: Always drush cex -y + commit after UI changes. Make it a Git hook or alias: gcx="git add config/default/ && git commit -m && drush cex".acquia+1

Direct Prod/Stage Edits

Pitfall: Someone tweaks a view on prod → next cim from Git overwrites it. Chaos ensues.

Fix: Lock UI config pages on prod/stage (permissions). Train: "UI changes only on DDEV/dev." Post-deploy cst verifies clean.acquia+1

Missing cex After Module/Core Updates

Pitfall: composer update drupal/views adds schema → cim fails on deploy with "unknown config keys."

Fix: Ritual: composer update → drush updb → drush cex → commit.[docs.acquia]​

Wrong Sync Directory

Pitfall: Using Drupal default ../config/sync/ instead of Acquia's ../config/default/.

Fix: Set $settings['config_sync_directory'] = '../config/default'; in settings.php. Verify: drush cst shows correct path.acquia+1

Config Split Misconfiguration

Pitfall: Split activates wrong env (dev gets prod caches) → performance disasters.

Fix: Test splits locally: drush gcs local; drush cex; drush cst. Verify settings.php $acquia_env logic matches Acquia vars.acquia+1

DB Sync Without Config Reset

Pitfall: Copy prod DB to dev → prod-only config breaks dev (no devel split).

Fix: After DB import: drush cim -y to restore Git baseline + activate dev split.[docs.acquia]​

Uncommitted Splits or Ignored Config

Pitfall: config/split/prod/ or config_ignore.settings not in Git → CI deploys partial config.

Fix: git add config/ recursively. CI job: drush cex && git diff --exit-code config/ (fails dirty).[acquia]​

Varnish/CDN Stale After cim

Pitfall: Config changes (new field) not visible due to cached pages.

Fix: Hook: drush p:invalidate everything after cim (your earlier question).[docs.acquia]​

Team Workflow Gaps

Pitfall: Junior devs skip cex or edit YAML by hand → merge conflicts, broken imports.

Fix:

  • PR template: "Did you drush cex -y?"

  • Pre-commit hook checks drush cst --format=json | jq '.out_of_sync' | grep -q 0

  • Single dev env as "config authority."[acquia]​

Quick Audit Commands

Run these weekly to catch drift:

text
drush @mysite.prod cst # Shows uncommitted changes drush @mysite.prod cex --preview # Dry-run export diffs git diff HEAD~1 config/default/ # Last deploy changed what?

Root cause of 90%: Treating config like "data" instead of code. Enforce Git→DB→verify everywhere.acquia+1
























 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Drupal configuration drift happens when the active DB config (config table) diverges from your Git YAML baseline (../config/default/), often from uncommitted UI changes or manual edits.tothenew+1

Detect it with drush cst and prevent it through CI/CD enforcement, hooks, and splits tailored to your Acquia/DDEV workflow.

Detection Methods

Primary command: drush cst (config status)

text
drush @mysite.prod cst # Lists out-of-sync items drush @mysite.prod cst --format=json | jq # Scriptable output

Shows exactly what config in DB differs from Git YAML (e.g., views.view.frontpage). Clean = no drift.[docs.acquia]​

Other checks:

  • drush cex --preview (dry-run export diffs)

  • drush cim --preview (dry-run import conflicts)

  • UI: /admin/config/development/configuration[tothenew]​

Prevention Strategies

1. Git as Source of Truth (Workflow)

  • Export only from dev/DDEV: UI change → drush cex → git commit.

  • Never edit config directly on stage/prod—cim will overwrite.acquia+1

  • Single direction: dev → git → all envs.

2. CI/CD Pipeline Gates

Every PR/deploy runs:

text
drush cst --format=json | jq '.out_of_sync' | grep -q 0 || exit 1 # Fail if dirty drush cim --yes drush cex --yes git diff --exit-code config/default/ # Fail if config changed

Catches "forgot to cex" before merge.[configu]​

3. Acquia Deploy Hooks (Automation)

docroot/hooks/deploy/post-code.sh:

bash
#!/bin/bash drush cim -y drush cr drush p:invalidate everything # Your Varnish drush cst --format=json | jq '.out_of_sync' | grep -q 0 || echo "DRIFT DETECTED"

Runs automatically on every code deploy.acquia+1

4. Config Split for Env Differences

Prevents "prod-only" config leaking:

text
config/default/ # 99% shared config/split/prod/ # Prod-only (SES, caches) config/split/dev/ # Devel modules

settings.php activates correct split per $AH_SITE_ENVIRONMENT.acquia+1

5. Prod Lockdown

  • Permissions: Remove "Administer site configuration" from prod roles.

  • Audit script (cron/nightly):

    bash
    drush @mysite.prod cst --format=json | jq > /tmp/prod-config-drift.json # Slack/email if out_of_sync > 0

6. Post-DB-Sync Reset

After dev DB ← prod DB copy:

text
drush cim -y # Restore Git baseline + dev split

Prevents prod config breaking dev tools.[docs.acquia]​

Daily Commands for Your Workflow

text
# Before feature work ddev drush cst # Start clean # After UI change ddev drush cex -y git add config/default/ git diff --name-only config/default/ | wc -l # Verify changes # Pre-PR/deploy drush cst # Must be 0 # Post-deploy verification drush @mysite.prod cst

Drift Recovery

If cst shows differences:

text
1. drush cex --preview # See what you'd overwrite 2. Decide: commit the drift? → drush cex → git commit 3. Or reset: drush cim -y # Force Git baseline

90% of drift = human error. Automate checks, train "cex after every UI change," and your Acquia + DDEV + Behat setup stays pristine.acquia+1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 






















Drupal’s configuration management is basically “all config lives in YAML in Git, and you deliberately push that into databases,” and your process design is what keeps environments from drifting.chromatichq+3

Core concepts (mental model)

  • Two config states: active config in the DB vs exported config in the filesystem under $settings['config_sync_directory'] (usually ../config/sync).tothenew+2

  • Config is things like content types, fields, views, permissions, and module settings, not content entities.umn+1

  • The only “source of truth” that scales is the exported YAML in Git, not whatever happens to be in prod’s DB today.chromatichq+2

Version control practices

  • Always commit the entire config/sync directory to Git and treat it as first‑class code.acquia+3

  • Every feature branch that changes behavior should include both PHP/Twig and the corresponding YAML changes in the same commit series.pantheon+1

  • Use normal Git practices for config: code review, diffs on YAML, small focused changes, and avoid long‑lived branches that accumulate conflicting config.configu+1

  • Never hand‑edit YAML in Git except to resolve conflicts; the normal flow is change via UI, export, commit.specbee+1

Environment workflow (to avoid chaos)

High‑level “golden path”:

  1. Make config changes only on a canonical dev environment (local or shared).umn+2

  2. Export: drush cex (or UI) writes the active DB config to YAML in config/sync.drupal+1

  3. Commit and push the YAML to Git; CI runs tests.linkedin+2

  4. Deploy the code (with YAML) to higher envs, then run drush cim there to import into the DB.tothenew+2

Key guardrails to prevent drift:

  • Never do “drive‑by” config edits directly on staging/prod; if someone does, that change will be overwritten on the next cim.linkedin+2

  • Lock down permissions so only automation (or ops) can run cim on prod and only a few people can change config on the canonical dev.acquia+1

  • Before each deploy, check drush cst (config status) or the UI diff to ensure the target environment has no unexpected local changes.specbee+2

Handling environment differences

You still need some config to vary (e.g., dev mail catchers, logging, test modules). The pattern is: make those differences explicit instead of letting them become random drift.

  • Use modules like Config Split to have separate splits for dev, stage, prod, each with its own directory and inclusion rules.chromatichq+1

  • Keep environment selection in code/settings (e.g. settings.php enabling the right split based on env vars), not in the UI.tothenew+1

  • Put truly environment‑local things in settings, not config (e.g. DB creds, API keys, some performance flags), so they’re outside the config sync system entirely.pantheon+1

Ops/CI perspective against config drift

From an ops standpoint, you’re trying to enforce “only Git‑driven config is allowed”:

  • CI/CD pipelines deploy code then automatically run drush cim --yes on the target env as a step, with smoke tests afterwards.configu+2

  • Pipelines fail if there is uncommitted config (e.g., a job that runs drush cex and checks git status clean) so no one can sneak UI‑only changes.pantheon+1

  • Regularly re‑apply the Git baseline (e.g., nightly cim dry‑run or diff) to detect and correct any manual tweaks that snuck into prod.cloud.google+1

A practical high‑level rule set that avoids chaos:

  • One canonical “authoritative” environment for new config, everything exported from there.umn+1

  • All config stored in Git, reviewed, and deployed via CI/CD.linkedin+2

  • No direct config edits on prod; imports there only from the main branch’s YAML.acquia+2

  • Use explicit tools (Config Split, settings.php) for environment‑specific differences, not ad‑hoc changes in UIs.chromatichq+2

If you want, I can sketch a concrete Acquia‑style workflow with settings.php, config_split, branch strategy, and “who is allowed to run cim where” tuned to your DDEV → Cloud Next setup.