Sign inSign up

trailro26/hass-remote-integration

By trailro26

•Updated 1 day ago

Run one Home Assistant integration in its own container, mirrored to your HA over MQTT

Image
0

3.9K

trailro26/hass-remote-integration repository overview

⁠hass-remote-integration

CI Image Docker Hub Buy me a coffee

Run one Home Assistant custom integration in its own small container, outside your main Home Assistant, and bring what it produces back to your main HA over MQTT: entities (with MQTT discovery), services and a health signal.

A web UI does the rest: install versions from GitHub, configure, start and roll back, back up and restore, move settings over from an existing Home Assistant, and cut over. No HACS, no HA frontend, no shell.

Nothing in the manager is specific to one integration: any custom integration published as GitHub releases, or sitting in a local directory, can run this way.


⁠Why would I want this?

Custom integrations that talk to hardware are often the fragile part of a Home Assistant install: an HA upgrade breaks them or they pin a conflicting library, an update migrates their config and going back is painful, and a bug in one (a stuck event loop, a leaking serial port) slows down or restarts the whole house. A container of its own decouples the integration:

  • Independent versions. The container pins its own Home Assistant Core and libraries. Your main HA upgrades freely: it only sees MQTT.
  • Reversible updates. Versions live side by side; every switch takes a backup first, is smoke-tested, and can roll back automatically.
  • Isolation. A crash or a hang stays in its container.
  • Safe migration. Run it in shadow mode next to your main HA, compare entity by entity, then switch over with one click, and undo with one click.

It is not a replacement for Home Assistant: no frontend, no automations, no recorder. It runs exactly one integration and publishes it.


⁠How it works

+--------------------------- container -----------------------------+
|  entrypoint.py   installs Home Assistant Core into a venv on the  |
|                  volume, applies scheduled restores, falls back   |
|                  to the previous HA after repeated failed boots   |
|                                                                   |
|  run.py          headless Home Assistant Core: loader, registries,|
|                  http on :8087, the manager, the integration      |
|                                                                   |
|  integration_manager  web UI + API: versions, config, patches,    |
|                  MQTT translator + discovery, health, backups,    |
|                  import, parity & cutover, diagnostics            |
+-------------------------------+-----------------------------------+
                                | MQTT: hass_<domain>/...
                                v
                     +----------+----------+       +---------------------+
                     |     MQTT broker     | <---> |  your main Home     |
                     |    (e.g. mosquitto) |       |  Assistant (MQTT    |
                     +---------------------+       |  integration)       |
                                                   +---------------------+
  • Home Assistant is not baked into the image. A fresh volume installs the newest stable release from PyPI into /config/venv-<version>; later versions are picked in the UI, and the previous one is kept for rollback.
  • The container holds one integration, with any number of its versions in a version store. For a second integration, run a second container.
  • Everything it publishes is named after the integration: MQTT base topic and client id hass_<domain>, discovery ids hass_<domain>-... (a volume that published with 0.26.0 or older keeps its hass_<domain>_...: Id format⁠). Containers of different integrations share one broker and one main HA without clashing. Two containers of the same integration share them only with different HRI_INSTANCE values (hass_<domain>-<instance>); with the same names they would take each other's connection and clear each other's retained data. A container that already published keeps its names (Identity⁠).

⁠Requirements

  • Docker with Compose, or Home Assistant OS / Supervised (as an app, see below⁠). Supervised installs are deprecated and unsupported by Home Assistant.
  • An MQTT broker reachable from the container (mosquitto or any other).
  • For the entities to appear in your main Home Assistant: its MQTT integration, on 2025.10 or newer (2026.5 for date, time and datetime entities; see what it needs⁠). The container installs its own, separate Home Assistant.
  • Access to the hardware your integration needs: a USB/serial device passed into the container, or a network bridge (see Hardware access⁠).

Footprint: roughly 170–210 MB of RAM with a typical integration running, and about 800 MB of disk per installed Home Assistant version.


⁠Quick start

The image is built for amd64 and arm64 (a Raspberry Pi with a 64-bit OS, Apple silicon, most NAS boxes). Both registries get the same tags and the same digest: every release as <version>, the newest stable release of each <major>.<minor> series also under that tag, and the newest stable release as latest:

RegistryImage
GitHub Container Registry (the default; since 0.9.0)ghcr.io/trailro/hass-remote-integration⁠
Docker Hub (since 0.16.0)trailro26/hass-remote-integration⁠

Docker Hub limits pulls from hosts that are not logged in; either registry is fine for one container.

Get the compose file of the latest release, in a directory of its own:

mkdir hass-remote-integration && cd hass-remote-integration
curl -fsSLO https://github.com/trailro/hass-remote-integration/releases/latest/download/docker-compose.yml

Put your settings in a .env file next to docker-compose.yml:

TZ=Europe/Berlin          # your time zone
HRI_PORT=8087             # port of the UI
# HRI_VERSION=0.28.4      # optional: pin a release (default: latest)
# HRI_REGISTRY=docker.io/trailro26  # optional: pull from Docker Hub (default: ghcr.io/trailro)
# HRI_PASSWORD=...        # optional: require a password for the UI and API
# HRI_APT_PACKAGES=ffmpeg  # optional: Debian packages installed at boot (what pip cannot install)

If your MQTT broker runs in Docker, the container must reach it. Put what is specific to your machine in a docker-compose.override.yml, which Compose loads automatically. For a broker on an existing Docker network called my-mqtt-network:

services:
  hass-remote-integration:
    networks:
      - my-mqtt-network

networks:
  my-mqtt-network:
    external: true

A broker elsewhere on your LAN needs nothing: use its IP address on the MQTT page. Then start it (to build the image yourself instead, see Development⁠):

docker compose up -d

Open http://<docker-host>:8087. On the very first start the page says Home Assistant is being installed (without a password, with its progress); it takes a few minutes.

⁠Home Assistant OS / Supervised

This repository is also an app repository (Supervised installs are deprecated and unsupported by Home Assistant):

  1. Settings > Apps > App Store, menu ⋮ > Repositories, add https://github.com/trailro/hass-remote-integration.
  2. Install hass-remote-integration, set a password on its Configuration tab, and start it.
  3. Open Web UI, or HRI in the sidebar: through Home Assistant's login, remote access included. The first start installs Home Assistant inside the app, which takes a few minutes and needs internet access.

The Mosquitto broker app is reachable as core-mosquitto, the broker host a fresh app offers. Access (ingress_users, the app's port), options, backups, serial devices and updates: docs/app.md⁠.


⁠Your first integration, step by step

The UI has one page per task:

PageWhat you do there
OverviewWhat runs and its health, start/stop, restart, notifications, the timeline
IntegrationVersions and releases, preflight, patches, YAML config, config flow, config entries
InstallInstall from the registry or any GitHub repo, the environment builder, dev mode
MQTTBroker connection, translator status, discovery, recent commands, health rules
CutoverCompare with your main HA, enable discovery, undo
Entities / Devices / ServicesInspect, rename, disable, call services
Logs / Log filesThe integration's logs and the log files it writes
SystemHome Assistant version, backups and restore, import from a HA backup, settings, diagnostics
⁠1. Install the integration

On Install, pick an integration from the registry and click Install latest stable. For one that is not in the registry, Find an integration searches the default HACS list of custom integrations by name, domain, repository, description and topics; Use adds the hit to the registry and selects it in the environment builder. Or open add a repo to the registry and give a domain and a GitHub owner/repo whose releases contain custom_components/<domain>/. Nothing runs yet: the version sits in the version store.

For more control, the environment builder on the same page takes the integration, any release, branch or commit, and a Home Assistant version. Check resolves the ref to a commit, downloads it into a scratch directory, resolves its requirements with pip --dry-run (against the pins of the Home Assistant version chosen, not the running one's), evaluates patches, dependencies and the minimum HA version, and reports blockers and warnings. It installs nothing and leaves nothing on the volume, but building a source-only package runs its build code (setup.py, PEP 517 hooks), as an install would. Prepare installs exactly the commit Check saw, and adds a repository you typed in to the registry; if a branch moved since, Check again, and if GitHub cannot say which commit a ref points at, Prepare refuses. A release whose hacs.json declares a newer minimum Home Assistant cannot start on an older one: prepare it together with that Home Assistant version. A minimum that is not a plain version number (2024.1, 2025.1.0b1) is ignored.

⁠2. Configure it

Choose whichever fits the integration, on the Integration page:

  • Config flow runs the integration's own setup dialog, as HA's frontend would; start the integration first, since the flow is its code. Texts come from its translations/en.json (else raw schema keys), selectors render as their control (an unknown one as a JSON textarea; see API⁠), and a value the page cannot convert is refused under its field, with nothing sent. HA refuses a second flow for a device with already_in_progress: every flow it still holds, one abandoned by a page reload included, is listed with Continue and Abort, and Start config flow ends this page's flow first.
  • YAML config, for integrations configured in configuration.yaml: paste what would go under <domain>:. It is validated on save and applied at boot. When a later release imports that YAML into a config entry, a notification says so: remove the YAML then, because it is still applied at every boot.
  • Import from your existing Home Assistant (on System): upload a standard HA backup (the uncompressed .tar Home Assistant writes, encrypted or not). The installed integration's config entries come over with data and options, and entity ids, names, icons and disabled flags are aligned both ways, so entities keep their ids from your main HA. An import never goes next to an entry the integration already has: Import is refused with "already has a config entry here" (delete that entry; the upload stays), and Import all skips such integrations, lists them under skipped and deletes the upload once nothing failed (upload again to import a skipped one). An entry imported from the same backup before is skipped as "already imported"; the integration's other entries still come over. An import is refused while another import, a start, stop or install runs; a restart in the middle leaves either the imported entry with its store, or no entry and the volume's own store (details⁠).
⁠3. Start it

Click Start on Overview or Integration. The version is deployed, its requirements installed and patches applied, and the config entries the manager disabled (on a stop or a switch) are enabled again; an entry you disabled yourself stays disabled. A backup is taken first when something changes. A requirement that takes over 30 minutes to install is stopped with everything it started and reported as pip failed for: <requirement>. Starting another version than the deployed one runs its preflight first (see Updating the integration⁠); blockers ask whether to start anyway. If a start fails after the new files went out, the previous files are put back. When a start needs a process restart (another version of a loaded integration, or YAML), the page says so: use Restart process.

About five minutes after a start, a smoke test checks the health verdict. If the version switched to does not set up (a config entry in setup_error or migration_error, the integration not loaded, a setup that never finishes), the manager rolls back to the previous version and its backup automatically (configurable on System); an entry still in setup_retry gets one more interval first. A degraded version did set up: it is kept and reported. For an integration that breaks later, see the health watchdog⁠ (off by default).

⁠4. Connect MQTT

On MQTT, enter the broker host, port and credentials, tick enabled, save. The container publishes one retained JSON document per entity, the service catalog and a health document under hass_<domain>/.

Leave discovery off for now if your main HA still runs the same integration, or you get every entity twice. Turning it off later removes the announced entities from your main HA, like Undo on Cutover (the manager device stays while manager_discovery is on).

⁠5. Move over from your main Home Assistant

The recommended path is shadow mode: run the container next to your main HA for a while, with MQTT enabled and discovery off, and compare what the container announces with what your main HA has made of it. Shadow mode⁠ describes a complete setup, including a serial device shared by both instances through a TCP bridge.

  1. Make sure only one side talks to the hardware in a way that conflicts (for example, only one side sends commands to the devices).
  2. On Cutover, give your main HA's URL (no user:password@) and a long-lived access token (optional). The page matches the entities this container announces over MQTT with the MQTT entities your main HA created from that discovery, by unique id, and lists what is missing on either side and what differs in state, names and flags. The integration's own entities on your main HA are not part of the comparison: compare those by hand. The page only reads, and needs no administrator, but a token carries every right of its user: create it under a dedicated user without admin rights.
  3. When you are happy: remove the integration from your main HA (delete its config entries; disabling keeps its entity ids registered, and the Cutover page refuses then), then click Enable discovery on Cutover. With the main HA configured, it refuses while the integration has config entries there, or while an entity id about to be announced is held by anything but this container's own mirror of that very entity (it would arrive as _2; a mirror of another entity renamed onto the id blocks too), and names what blocks. It also refuses while a scheduled restore, rollback, version switch, import or running action would replace the configuration at the next restart, or a smoke test is pending: restart or wait first. Your main HA then creates the entities, and the page watches until all exist. Entity ids stay the same, but the unique ids are new (hass_<domain>-<entity id>; hass_<domain>_<entity id> on a volume that published with 0.26.0 or older), so areas, labels and custom names have to be set again.
  4. Changed your mind? Undo removes every discovery config again, so your main HA drops the entities. The manager device stays while manager_discovery is on, and the Undo answer says manager_device_kept.

The rest of the documentation (everyday operation, configuration, troubleshooting, and links to the MQTT, security, API and file references in docs/) is in the full README on GitHub⁠.

Tag summary

Content type

Image

Digest

sha256:a33b8c03c…

Size

42.3 MB

Last updated

1 day ago

docker pull trailro26/hass-remote-integration