Understanding hass.data, runtime_data, and Config Entry Data in Home Assistant
In Home Assistant, entry.data stores persistent user configuration, entry.runtime_data holds live integration objects during runtime, and hass.data provides a global dictionary for cross-platform access to those same objects.
Home Assistant Core uses a three-tier architecture to manage integration state across restarts and entity platforms. Understanding the distinction between config entry data, runtime data, and the global hass.data registry is essential for developing integrations that properly persist settings and share live clients or coordinators with multiple entities.
The Three Data Storage Layers
Config Entry Data (entry.data)
The data attribute of a ConfigEntry object contains the static configuration provided by the user during setup—values like IP addresses, API tokens, scan intervals, or authentication credentials. This data is persisted to disk in Home Assistant's config_entries.json storage and remains immutable during runtime.
According to the source in homeassistant/config_entries.py, this dictionary is loaded at startup and used to reconstruct integrations. Modifications require calling hass.config_entries.async_update_entry(), which triggers a reload and rebuilds runtime objects from the new configuration.
Runtime Data (entry.runtime_data)
Introduced to standardize how integrations store live objects, entry.runtime_data holds transient, integration-specific instances like API clients, WebSocket connections, or DataUpdateCoordinator objects. This attribute is not persisted—it is initialized during async_setup_entry and destroyed when the entry is unloaded.
In homeassistant/components/zwave_js/__init__.py, the integration constructs a ZwaveJSData dataclass containing the client and driver events, then assigns it directly to the entry:
entry_runtime_data = ZwaveJSData(client=client, driver_events=driver_events)
entry.runtime_data = entry_runtime_data
This pattern allows entities to access live objects via config_entry.runtime_data without importing global state.
Global Storage (hass.data)
The hass.data dictionary serves as a global registry where integrations can store arbitrary data under their domain key. While runtime_data is the modern canonical location for entry-specific objects, many integrations mirror the same data into hass.data[DOMAIN][entry.entry_id] to facilitate access from entity platforms that receive hass and entry_id but not the full ConfigEntry instance.
In homeassistant/components/zwave_me/__init__.py, the controller is stored in both locations:
hass.data.setdefault(DOMAIN, {})
controller = hass.data[DOMAIN][entry.entry_id] = ZWaveMeController(hass, entry)
Similarly, homeassistant/components/youtube/__init__.py stores a dictionary containing the coordinator under hass.data[DOMAIN][entry.entry_id], allowing sensor platforms to retrieve it during entity setup:
coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR]
How the Layers Interact During Lifecycle
Setup Flow
When Home Assistant loads an integration, it executes async_setup_entry with the persisted ConfigEntry. The typical workflow follows this pattern:
- Read static config from
entry.data(host, API key, etc.) - Initialize live objects (clients, coordinators) using that config
- Store in runtime_data by assigning to
entry.runtime_data - Mirror to hass.data (optional) under
hass.data[DOMAIN][entry.entry_id]for platform access - Forward entry to entity platforms via
async_forward_entry_setups
Update and Reload
When configuration changes (e.g., user updates a setting via the UI), Home Assistant calls async_update_entry, which:
- Writes new values to
entry.dataand persists to JSON - Triggers an entry reload (unload + setup)
- Destroys the old
runtime_dataandhass.dataentries - Rebuilds live objects from the new
entry.data
await hass.config_entries.async_update_entry(
entry,
data={**entry.data, CONF_SCAN_INTERVAL: 30},
)
Unload
During async_unload_entry, integrations must clean up hass.data to prevent memory leaks, while runtime_data is automatically discarded by the ConfigEntry machinery:
# Cleanup pattern in async_unload_entry
hass.data[DOMAIN].pop(entry.entry_id)
Key Implementation Files
| File | Purpose |
|---|---|
homeassistant/config_entries.py |
Defines ConfigEntry class with data and runtime_data attributes; implements persistence and reload logic. |
homeassistant/components/zwave_js/__init__.py |
Demonstrates modern runtime_data pattern using a dataclass to store client and driver events. |
homeassistant/components/zwave_me/__init__.py |
Shows legacy hass.data storage pattern for controller access across platforms. |
homeassistant/components/youtube/__init__.py |
Illustrates hybrid approach storing coordinator in hass.data dictionary for entity retrieval. |
Summary
entry.datacontains persisted user configuration (host, credentials, options) that survives restarts and is stored in JSON.entry.runtime_dataholds live, non-persisted objects (API clients, coordinators) created during setup; it is the modern canonical location for entry-specific runtime state.hass.dataprovides a global dictionary for cross-platform access, often mirroring the same objects stored inruntime_datato facilitate entity setup without passing ConfigEntry instances.- Updates flow through
async_update_entry, which modifiesentry.data, triggers a reload, and rebuilds runtime objects from the new configuration.
Frequently Asked Questions
What is the difference between entry.data and entry.runtime_data?
entry.data stores static configuration values like IP addresses and API keys that are written to disk and reloaded on startup. entry.runtime_data stores live objects like API clients or data coordinators that exist only while Home Assistant is running and are recreated during the setup phase. While entry.data persists across restarts, entry.runtime_data is ephemeral and destroyed when the integration unloads.
When should I use hass.data instead of runtime_data?
Use hass.data when entity platforms need to access integration-specific objects but only receive hass and entry_id during setup, not the full ConfigEntry instance. This pattern allows platforms to retrieve coordinators or controllers via hass.data[DOMAIN][entry_id]. However, if entities have direct access to the ConfigEntry object, prefer entry.runtime_data as it provides type safety and avoids global state pollution.
How do I update configuration values for a config entry?
Modify entry.data by calling hass.config_entries.async_update_entry(entry, data=new_data). This method persists the new configuration to JSON storage and automatically triggers a config entry reload, which unloads the current runtime objects and rebuilds them from the updated configuration. Never mutate entry.data directly, as this bypasses persistence and reload logic.
What happens to runtime_data when an integration reloads?
When a configuration entry reloads (either manually or via async_update_entry), Home Assistant calls async_unload_entry, which destroys the current runtime_data object. The integration then re-executes async_setup_entry, creating a fresh runtime_data instance from the current entry.data. This ensures that runtime objects always reflect the latest configuration and that no stale connections or state persist across reloads.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →