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/*.ymlprovides 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.
$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/*.ymland toconfig/install/*.yml, but you forget thehook_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.ymlprovides 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
NULLfrom->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(), sinceconfig/installonly 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.