Using hook_update_N() for configuration changesfor Drupal 8 , 9 , 10 , and 11

Last updated :  

The previous lesson ended with a real limitation: config/install/ only runs once, at install time. So what do you do six months later, when the weather module already has thousands of live installs and you need every one of them to gain a new units setting, or rename an existing key? You can't ask every site to reinstall the module. This is exactly the problem hook_update_N() exists to solve.

What you'll learn in this lesson

  • What hook_update_N() is and when Drupal actually runs it
  • The naming convention behind that mysterious N
  • How to safely add a new key to already-active configuration
  • Why update hooks must be idempotent-safe and never depend on module code that might change later

Adding a new setting to already-installed sites

Say the weather module needs a new units setting (metric or imperial) added to every site that already has it installed. That goes in weather_module.install:

/**
 * Add a default 'units' setting to existing weather_module installs.
 */
function weather_module_update_10001() {
  $config = \Drupal::configFactory()->getEditable('weather_module.settings');
  if ($config->get('units') === NULL) {
    $config->set('units', 'metric')->save();
  }
}

The number after update_ is a version number you choose yourself — convention is a 4-digit number where the first digit tracks your module's own major release (10001, 10002, ...). Drupal keeps a per-module record of the highest update number that's already run on a given site, in the key_value table. Running drush updb executes every update function whose number is higher than what's already recorded, in ascending order, then bumps that stored number forward — so each one runs exactly once per site, ever.

Why the get('units') === NULL check matters

Update hooks need to be safe to re-run without corrupting anything, because a failed drush updb partway through is a real scenario a site administrator will eventually hit and re-run. Checking whether units is already set before overwriting it means running this function twice by accident doesn't clobber a value a site builder may have already changed in between.

Update hooks run before your module's own code is guaranteed current. Don't call service classes, plugin managers, or anything from your own module's more recent code inside an update hook — the codebase has already been updated, but the running request may still be mid-transition. Stick to low-level API calls like \Drupal::configFactory() and \Drupal::database(), exactly as shown above.

Quick check: a site already has weather_module_update_10001() recorded as run. You add a new weather_module_update_10001() — reusing the same number — with different logic inside it. Will it run on that site? (Answer: no. Drupal tracks the highest number already executed per module; reusing a number that's already run means it will never fire again on sites that passed it. Always use a new, higher number for new update logic — never edit an update function that's already shipped.)

Key takeaways

  • hook_update_N() lets you change configuration (or run any one-time migration logic) on sites where your module is already installed — config/install/ alone can't reach them.
  • The N is a version number you assign; Drupal runs every update function numbered higher than what's already recorded for that module, in order, exactly once.
  • Never reuse or edit an update number that's already shipped — always add a new, higher one for new logic.
  • Guard update hooks against re-running destructively (check before overwriting), and avoid depending on your module's own higher-level code inside them.

Coming up next

That closes out configuration management — the weather module can now be configured, read from anywhere, ship with sensible defaults, and evolve safely on live sites. Next, we turn to Forms: building the actual settings form UI that lets a site builder set that api_key and default_location in the first place.