Skip to content

Organize Home Assistant Before Your YAML Becomes Unmanageable

06/11/2026
Organizing Home Assistant YAML files

This article may contain affiliate links. If you buy through these links, the price is the same for you and the store pays me a small commission that helps keep Tecnoyfoto running.

← Chapter 9 · Course index

A configuration does not become difficult to maintain simply because it has many files. It becomes difficult when we do not know what each file contains, what structure Home Assistant expects at the point where it is included, or which files the user interface may rewrite.

So far, we have worked with standalone automations and scripts so that we could focus on their behavior. In this chapter, we will put those pieces into a real configuration structure. We will learn the role of configuration.yaml, the difference between including one file and merging a directory, and when a package is clearer than separating everything by domain.

There is no universally perfect organization. A small home can run for years with automations.yaml and scripts.yaml. The goal is to choose a structure you understand and can expand without turning every change into a blind search.

configuration.yaml remains the entry point

Although many integrations are configured through the user interface, configuration.yaml remains the entry point for the YAML configuration. A typical installation contains entries such as these:

default_config:

automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml

These lines do not blindly copy text. Each !include is evaluated exactly where it appears, so the included file must provide the type of structure expected at that location.

In the example:

  • automations.yaml must contain a list of automations.
  • scripts.yaml must contain a map whose keys identify the scripts.
  • scenes.yaml must contain a list of scenes.

The difference between a list and a map is not a formatting detail. It determines whether each entry begins with a dash or with a key.

DEALS · OFFICIAL STORE

Discounts on SONOFF smart home

Wi-Fi switches, relays, sensors, LED strips and more. Deals change frequently in the official store.

Coupon: TECNOYFOTO (10% off at checkout)

View official deals Affiliate link Affiliate link · SONOFF store

Do not repeat the key that already exists outside

Suppose configuration.yaml contains:

automation: !include automations.yaml

Then automations.yaml starts directly with the list:

- alias: "YAML Course - Turn on when opening"
  triggers:
    - trigger: state
      entity_id: binary_sensor.course_front_door
      from: "off"
      to: "on"
  actions:
    - action: light.turn_on
      target:
        entity_id: light.course_living_room

It should not begin like this:

automation:
  - alias: "YAML Course - Turn on when opening"

The automation key is already present in the main file. Repeating it is conceptually equivalent to building automation → automation → list, which is not the structure Home Assistant expects.

Something similar happens with scripts, but the expected content is a map:

prepare_living_room:
  alias: "YAML Course - Prepare the living room"
  sequence:
    - action: light.turn_on
      target:
        entity_id: light.course_living_room

There is no dash before prepare_living_room. That key creates the script.prepare_living_room entity.

!include loads one file; directory variants build structures

When a single file becomes unwieldy, Home Assistant provides four directory directives. Their names look similar, but they are not interchangeable.

!include_dir_list: each file becomes one item

automation manual: !include_dir_list automations/manual

This directive returns a list and uses the complete contents of each file as one list item. Each file must therefore contain one automation without an outer dash:

alias: "YAML Course - Entry light"
triggers:
  - trigger: state
    entity_id: binary_sensor.course_front_door
    to: "on"
actions:
  - action: light.turn_on
    target:
      entity_id: light.course_living_room

This is useful when we want one automation per file.

!include_dir_merge_list: every file contributes items to one list

automation manual: !include_dir_merge_list automations/manual

This directive also returns a list, but now each file must contain its own list. In other words, every automation does begin with a dash. One file can contribute one or more automations:

- alias: "YAML Course - Open door"
  triggers:
    - trigger: state
      entity_id: binary_sensor.course_front_door
      to: "on"
  actions:
    - action: light.turn_on
      target:
        entity_id: light.course_living_room

This option works well when each file groups several related automations.

!include_dir_named: the file name becomes a key

This directive returns a map. Each file name, without extension, is used as a key and its content as a value.

homeassistant:
  packages: !include_dir_named packages

A packages/yaml_course.yaml file is loaded under the package name yaml_course. Its contents can bring together several domains related to the same function.

!include_dir_merge_named: combines several maps

This variant also returns a map, but it does not use the filename as a key. It merges the keys already defined inside each file:

group: !include_dir_merge_named groups

One file could define living_room: and another could define exterior:. The result is a single map containing both keys. Repeating a key in two files makes ownership ambiguous, so each key should have a single owner.

All four directives also traverse subdirectories and process files in alphabetical order. Directory includes process files with the .yaml extension; a .yml file is not included automatically. Order can matter when one sequence depends on an earlier one, but it should not be used to conceal hard-to-understand dependencies.

Keep UI-managed and manual automations separate

The Home Assistant user interface normally manages automations.yaml. When you edit an automation in the visual editor and save it, Home Assistant may rewrite how that file is formatted. It is not the right place to enforce a custom layout manually or store comments that must be preserved.

We can keep both workflows by using labeled domain keys:

automation ui: !include automations.yaml
automation manual: !include_dir_merge_list automations/manual

The words ui and manual are labels that let us declare multiple configurations for the same domain. Automations in automations.yaml remain editable from the user interface; manually maintained automations stay in our own files and appear as read-only configurations.

You do not need to split anything simply because the option exists. If you have twelve automations and can find any of them in seconds, one file may still be the clearest solution.

Packages: group by function instead of separating by domain

Separating by domain puts automations in one place, scripts in another, and helpers somewhere else. That is technically consistent, but it can scatter one complete home function across several files.

A package lets you collect everything related to a room or purpose in one file:

homeassistant:
  packages: !include_dir_named packages

Within packages/yaml_course.yaml we can find, for example:

input_boolean:
  course_someone_home:
    name: "YAML Course - There's someone at home"

script:
  course_prepare_living_room:
    alias: "YAML Course - Prepare the living room"
    sequence:
      - action: light.turn_on
        target:
          entity_id: light.course_living_room

automation:
  - alias: "YAML Course - Entrance"
    triggers:
      - trigger: state
        entity_id: binary_sensor.course_front_door
        to: "on"
    actions:
      - action: script.course_prepare_living_room

The package contains complete domain keys because it is included at a different level. It does not merely provide the contents of automation; it contributes several sections to the configuration.

Organizing by room often works well when each space has largely independent behavior. Organizing by function—security, climate control, or lighting—helps when the same logic spans several rooms. What matters is giving each piece a predictable home instead of mixing several organizational schemes without a consistent rule.

secrets.yaml separates secrets, but does not encrypt them

When a configuration contains a password, token, or private URL, we can replace the value with a reference:

password: !secret course_example_password

And save the data in secrets.yaml:

course_example_password: "private-value"

This lets you share a configuration fragment without copying the secret and keeps sensitive values in one place. However, secrets.yaml remains plain text to anyone with access to the files. It is not encrypted, and it does not automatically make a backup that contains it safe to share.

There is another practical limitation: using !secret inside a user-interface-managed automations.yaml or scripts.yaml file prevents the editor from displaying or editing it normally. If an automation needs secrets, place it in a separate manually maintained file. Also remember that administrators may be able to see the resolved value in places such as the configuration source or a trace.

Before sharing configuration with a person or an AI:

  • Replace keys, tokens, webhooks, external addresses and credentials with fictitious values.
  • Also check people’s names, coordinates, license plates, and notification messages.
  • Don’t publish secrets.yaml.
  • If you accidentally share a secret, rotate it. Deleting it from the message does not guarantee that it is no longer exposed.

How to choose a structure without overcomplicating it

A reasonable progression would be:

  1. Keep the standard files as long as they remain manageable.
  2. Separate handwritten automations from those managed by the user interface.
  3. Create directories only when there is a clear grouping.
  4. Use packages for functions that include multiple domains and make sense to keep together.
  5. Document the chosen organizational approach in the system’s README.

Do not judge the quality of an organization by its file count. One hundred five-line files can be just as difficult to navigate as one five-thousand-line file if their names tell you nothing.

Exercise: identify the structure returned by each include

The practice environment contains four situations. Before looking at the solution, answer these questions for each one:

  1. Does Home Assistant expect a list or a map where the directive appears?
  2. Does each file represent one item, contain a list, or provide keys for a map?
  3. Should the parent key appear inside the included file?
  4. Will the interface manage that file?

Then reorganize one manual automation and one script without changing their behavior. The goal is not to memorize four directive names, but to reason from the resulting structure.

See the reasoned solution

If each file contains a list that can provide multiple automations, use !include_dir_merge_list. If each file contains one automation without an outer dash, use !include_dir_list.

To merge keys already defined in several maps, use !include_dir_merge_named. To use each filename itself as a key—as with a package directory—use !include_dir_named.

The proposed structure looks like this:

automation ui: !include automations.yaml
automation manual: !include_dir_merge_list automations/manual
script: !include scripts.yaml

Therefore, automations/manual/entry.yaml keeps the dash in - alias: because it provides a list. scripts.yaml begins directly with course_prepare_living_room: because the script: key is already present in configuration.yaml.

What you should be able to explain now

At the end of this chapter, you should be able to open configuration.yaml and follow the path to the file containing an automation. You should also distinguish these ideas:

  • !include occupies a specific place and the file returns the expected content there.
  • list and named are not aesthetic preferences: they produce different types of data.
  • A package brings together several domains around a function or space.
  • Files managed by the user interface follow different rules from manually maintained files.
  • secrets.yaml separates sensitive information, but it does not encrypt it.

In the next chapter, we will use this structure to diagnose problems systematically. Once we know which file provides each block, an error stops being “something broken in Home Assistant” and gains a specific location and layer.