Providing Default Settingsfor Drupal 8 , 9 , 10 , and 11

Last updated :  

Every appliance you buy ships with factory settings — a thermostat doesn't refuse to work until you've configured it, it just starts at a sensible temperature and lets you change it later. Your module's configuration deserves the same courtesy. Without a genuine default value, a freshly installed module either crashes the moment it tries to read a setting that's never been set, or silently behaves as if every setting is empty — neither is acceptable for something a real user is expected to install and use immediately.

What you'll learn in this lesson

  • How config/install/*.yml provides a module's very first configuration values
  • Why the schema file and the install file must agree on every key
  • What happens if you skip a default entirely
  • How to add a new default setting to a module that's already installed on real sites

The default configuration file

# config/install/config_simple_example.settings.yml
message: 'Hello, Drupal!'

This file, from the real config_simple_example module in Drupal's Examples project, is copied verbatim into the site's active configuration the instant the module is enabled — before any human has ever seen the settings form. Its filename must exactly match the config object name it's providing defaults for (module_name.settings, here config_simple_example.settings), and Drupal discovers it automatically; there's no hook to implement.

Schema and install file must agree

# config/schema/config_simple_example.schema.yml
config_simple_example.settings:
  type: config_object
  label: 'Example config'
  mapping:
    message:
      type: text
      label: 'Message'

Every key present in the install YAML must have a matching entry in the schema's mapping — the schema declares message's type (translatable text, here), the install file provides its actual starting value. Miss a key in either file and Drupal's configuration inspector (and any strict-schema-checking CI pipeline) will flag it; a real, deployed default without a schema entry is a genuine bug, not just a warning to ignore.

What happens if you skip the default entirely: the config object simply doesn't exist until something writes to it. Any code that calls $this->config('your_module.settings')->get('some_key') before that point gets back NULL, silently — no error, no warning. If your module's logic doesn't explicitly handle a NULL default, that's exactly the kind of bug that only shows up on a fresh install, not in your own dev environment where you've long since saved the settings form once.

Adding a new default to an already-installed module

config/install/*.yml only runs once — at initial module installation. If you later add a brand-new setting to a module that real sites already have enabled, editing config/install/*.yml alone does nothing for them; that file won't be re-read. You need an update hook:

/**
 * Add the new 'show_banner' setting with a sensible default.
 */
function config_simple_example_update_10001() {
  \Drupal::configFactory()->getEditable('config_simple_example.settings')
    ->set('show_banner', FALSE)
    ->save();
}

This runs once, the next time anyone executes drush updatedb on a site that already has the module installed — exactly the mechanism that lets existing sites pick up new defaults without an administrator manually re-entering settings that should've just worked.

Quick check: you add a new key to config/schema/*.yml and to config/install/*.yml, but you forget the hook_update_N(). What happens on a site that already had the module installed before your change? (Answer: nothing changes for them — the schema and install file only affect fresh installs. Existing sites keep running without the new key until an explicit update hook sets it, or until someone manually re-saves the settings form.)

Key takeaways

  • config/install/module_name.settings.yml provides a config object's very first value, applied once, at module installation.
  • Every key in the install file needs a matching entry in the schema's mapping — mismatches are a real bug, not a style nitpick.
  • A config key with no default returns NULL from ->get() until something sets it — code that reads config should never assume a value is guaranteed to exist.
  • Adding a setting to an already-shipped module requires a hook_update_N(), since config/install only runs on fresh installs, never on existing ones.

Coming up next

You've now covered the full lifecycle of a module's own settings — building the form, saving it, validating it, and defaulting it sensibly. Next, we shift from configuration a site builder tunes to navigation: defining menu links so your module's pages actually show up where visitors expect to find them.