Run one Home Assistant integration in its own container, mirrored to your HA over MQTT
3.9K
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.
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:
It is not a replacement for Home Assistant: no frontend, no automations, no recorder. It runs exactly one integration and publishes it.
+--------------------------- 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) |
+---------------------+
/config/venv-<version>; later versions
are picked in the UI, and the previous one is kept for rollback.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).date, time and
datetime entities; see what it needs).
The container installs its own, separate Home Assistant.Footprint: roughly 170–210 MB of RAM with a typical integration running, and about 800 MB of disk per installed Home Assistant version.
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:
| Registry | Image |
|---|---|
| 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.
This repository is also an app repository (Supervised installs are deprecated and unsupported by Home Assistant):
https://github.com/trailro/hass-remote-integration.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.
The UI has one page per task:
| Page | What you do there |
|---|---|
| Overview | What runs and its health, start/stop, restart, notifications, the timeline |
| Integration | Versions and releases, preflight, patches, YAML config, config flow, config entries |
| Install | Install from the registry or any GitHub repo, the environment builder, dev mode |
| MQTT | Broker connection, translator status, discovery, recent commands, health rules |
| Cutover | Compare with your main HA, enable discovery, undo |
| Entities / Devices / Services | Inspect, rename, disable, call services |
| Logs / Log files | The integration's logs and the log files it writes |
| System | Home Assistant version, backups and restore, import from a HA backup, settings, diagnostics |
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.
Choose whichever fits the integration, on the Integration page:
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.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..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).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).
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).
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.
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._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.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.
Content type
Image
Digest
sha256:a33b8c03c…
Size
42.3 MB
Last updated
1 day ago
docker pull trailro26/hass-remote-integration