Loading...
E.G.

Home Assistant Migration: Old and New in Parallel

October 21, 2026
Table of Contents

This is the first post of a series about moving my Home Assistant setup from one instance to another. The old instance runs 2024.11.1 on an Azure VM, the new one runs 2026.8.2 in Docker on a Hetzner server (what that server costs is a separate post), and I wrote earlier about why I rebuilt instead of upgrading. The old instance controls KNX lights, covers and heating and reads a PV inverter over Modbus, in about 4,800 lines of YAML. Both instances run side by side for a while, and the new one takes over one area at a time. This post covers the first steps on the real house.

The rule for parallel operation

Both instances are connected to the same KNX bus. If the same automation runs on both, every action happens twice: covers get two commands and my phone gets two push notifications. So every automation I carry over arrives on the new instance with initial_state: false. My notes call that shadow mode. I switch per area and always in the same order. I turn the automation off on the old instance first and enable it on the new one afterwards. On the new one I also remove initial_state: false from the YAML, otherwise the automation is off again after the next restart.

# a package on the NEW instance: carried over, loaded, switched off
automation:
  - id: outdoor_light_on
    alias: "Timer: outdoor light on"
    initial_state: false   # shadow mode, the old instance still switches this
    mode: single
    triggers:
      - trigger: time
        at: input_datetime.outdoor_light_start
    actions:
      - action: switch.turn_on
        target:
          label_id: timer_outdoor_light

The areas are ordered by risk. Energy and PV came first, because that area mostly calculates and controls nothing on the new instance. Helpers and time-based automations follow, then light, covers and heating. Through all the steps in this post every automation that switches something in the house kept running on the old instance.

Reading sensors without writing to the bus

Automations on two instances can only be compared in a meaningful way if both see the same states. I carried over 166 of the 170 KNX entities, including the comments from the old config. Two things I left out on purpose. One is expose, because with it Home Assistant reports a state onto the bus by itself, and that is active sending. The other is four switches where an accidental tap in the new dashboard would have real consequences.

In current versions the KNX connection is a config entry and no longer YAML, so I set it up through the config flow over the REST API. The new instance got the second tunnel address of the KNX interface, and the old one keeps the first. Two things went differently from the plan. The config flow creates the connection with state polling enabled and starts it immediately. The option can only be changed afterwards in the options flow, and by then the new instance had already sent 96 read requests to the bus. Reads are harmless, and I had still planned to start without any.

The second surprise was that switching state polling off does not mean never reading. After the next restart there were another 52 read requests. A comment in the Home Assistant source spells it out as True -> expire 60; False -> init. Off therefore means reading once at start and never again afterwards. For zero read requests, every single entity needs sync_state: false, and then the states stay unknown until a telegram happens to come by. For shadow mode I kept init: one sync at start, while the ongoing polling stays with the old instance.

The KNX integration in this version stores telegrams in its own SQLite database, so I could count afterwards what the new instance had sent. The result was 148 read requests and 0 write commands.

PV needed a different approach, because the PV dongle tolerates only a few Modbus connections. The old instance has long been reading through a small Modbus cache server that polls the dongle every 10 seconds and serves several clients, and I attached the new instance to the same cache. All 17 sensors show the same values as on the old instance, with differences only in the last decimal place.

Same entity ids on both instances

Dashboards and automations of the old instance refer to entity ids. If the ids differ on the new instance, every reference has to be touched during the move. Ids are derived from names, and the old instance has years of manual renames. I wrote a small tool that reads the entity registry of both instances over the WebSocket API, matches entries by their unique_id and renames on the new instance. It renamed 28 entities, and afterwards 183 of 183 matched ids were identical.

Matching by unique_id alone is not enough for KNX. The integration uses the group address as unique_id, so a motion detector that switches a light and the light itself have the same unique_id in different domains. The first run would have renamed six motion detectors into lamps, and the key is now platform plus domain plus unique_id.

The first automation that runs on both instances

I expected light to be the biggest area, and Home Assistant turned out to have almost no logic there. The KNX motion detectors switch the lamps themselves, directly on the bus, so the light also works when Home Assistant is down. What Home Assistant had was 7 automations, one per detector, that write the time of the last motion into a helper for a dashboard overview, the pattern from this older post. The seven differed in two names only, so they became one automation that derives the target helper from the entity id of the triggering detector. The shortened example below lists two of the seven detectors. Each detector needs an existing date and time helper with the matching suffix.

# one automation instead of seven, active on both instances
automation:
  - id: remember_last_motion
    alias: "Remember last motion per detector"
    mode: parallel           # two detectors in the same second
    triggers:
      - trigger: state
        entity_id:
          - binary_sensor.motion_detector_a
          - binary_sensor.motion_detector_b
        from: "off"          # old: only to "on", which also matched unavailable -> on
        to: "on"
    actions:
      - action: input_datetime.set_datetime
        target:
          entity_id: >-
            {{ trigger.entity_id | replace('binary_sensor.motion_detector_', 'input_datetime.last_motion_') }}
        data:
          datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"

The trigger changed as well. The old automations reacted to on without a from, so they also fired when a detector changed from unavailable to on after a restart or a KNX reconnect. On the old instance, the last-motion value of one detector stood at exactly the second in which the KNX integration had been reloaded that evening.

This automation is active on both instances right away. It writes only to helpers of its own instance and sends nothing to the bus, so two instances recording the same thing do not interfere. I copied the helper values from the old instance over the REST API and waited for the next real motion, which came at 21:05:54. Both instances stored that motion with the same second. All 7 helpers were equal on both, but I had copied their starting values and had seen only this one event. That was the first point at which the new instance demonstrably did the same as the old one in real time. It had 11 automations at that point, most of them still in shadow mode.

The first full state comparison

Covers were the next area, and with 43 covers I had expected a large one. Home Assistant has no logic of its own there either. The covers are driven by KNX push buttons and central functions directly on the bus. I used the step for a comparison of all states between old and new instead, on a Saturday morning after the first night. Counting the entities both instances have in common, 51 of 51 lights had the same state, 11 of 11 thermostats, 43 of 43 covers, 32 of 33 binary sensors and 16 of 29 switches. The two daily energy counters matched to the decimal place. The one differing binary sensor is intended, a contact whose polarity I had corrected at the source on the new instance.

The comparison uncovered two gaps that exist on both instances. Of the 43 covers, 33 are unknown on both, because the ETS project has no position or end-position feedback assigned for those drives. At that point I assumed this could be fixed in ETS. Reading the actuator programs later showed that for most of these drives it is a hardware limit, which gets its own post in this series. And 25 switches have no status object, so Home Assistant shows the last switching value it saw on the bus, which may no longer be the real state. Where the two instances differ on these, the old one shows off and the new one shows unknown. I consider unknown the more accurate display, since the old off is a remembered value from some earlier time.

What must not run twice, and what is open

Notifications are a different case from the motion automation. A push at sunrise and sunset or on arriving home would arrive twice if both instances had it enabled, so those automations stay in shadow mode on the new one. Toggle commands are the most dangerous category in parallel operation. A doubled on for a light is harmless, and a doubled toggle produces the opposite of what was wanted, so such automations always get turned off on the old instance first.

This post does not show a completed switch of an area that acts on the house. Up to the comparison, the old instance was still doing all the switching. The missing feedback for 25 switches and 33 covers was open at that point. Four days later I set the read flag on the objects of the switch actuators in ETS, and since then Home Assistant reads the state of 52 lights and switches on their switching address at start.

Frequently asked questions

Can two Home Assistant instances use the same KNX interface?

In my setup yes. The new instance got the second tunnel address of the KNX interface and the old one keeps the first. The automatic search needs multicast and found no tunnel from the new instance, so the form went straight to manual entry.

Does switching off state polling stop all reads on the KNX bus?

No. The integration still reads once at start, which was 52 read requests here. Only sync_state: false on every entity removes those too, and then a state stays unknown until a telegram arrives.

Which automations can be active on both instances at once?

Only those that write to helpers of their own instance and neither control a device nor send a notification.

Questions or additions?

Comments run on GitHub Discussions, so you need a GitHub account to write one.

Related articles