Files
esh-pfi-infrastructure/stacks/esphome
vh 687c6999f3 fix(esphome): actually disable remote-build — two switches, only one closes the port
ha-dev found the WS API and tested the read half; this runs the write. But the
command they identified is the wrong half, which is worth recording because the
naming actively misleads.

  remote_build/set_offloader_settings {remote_builds_enabled: false}
      the OUTBOUND half — this dashboard sending builds to peers.
      Persists, reads back false, and leaves the receiver listening.

  remote_build/set_settings {enabled: false}
      the receiver-side master switch, per ReceiverController.set_settings's
      own docstring. Tears the listener down live, no restart needed.

Set both. Verified across a restart: 6055 absent, zero peer-link bind lines,
zero mDNS advertisements, both switches read back false. Persisted at
_remote_build.enabled in /config/.device-builder.json — which did not exist
until the flag was first changed, so 'no on-disk representation' was true only
of the default state.

ESPHOME_REMOTE_BUILD_HOST=127.0.0.1 is KEPT as a backstop rather than removed.
The off state now lives in one JSON file whose in-code default is enabled:True
(controllers/remote_build/_state.py) and whose module's stores soft-recover to
an empty model on a malformed blob rather than erroring — so a lost or corrupt
settings file silently re-enables remote-build. With the env var set, that
regression binds loopback instead of 0.0.0.0.

Also finishes deploy-stack.sh properly. This was patched three times in one
session because -a is -rlptgoD and a non-root identity cannot apply owner,
group, permissions OR times to a root-owned directory; each patch fixed one
letter and the next deploy failed on the next one, every time exiting 23 AFTER
a successful transfer. The rule is now written into the script: the deploy
syncs content, the conventions own metadata. --no-o --no-g --no-perms
--omit-dir-times. Verified: clean run, destination keeps 2775 root:docker with
setgid intact.
2026-09-14 19:05:11 -07:00
..

esphome — ESPHome Device Builder

Host: esh-docker-vm (10.0.50.45) · UI: http://10.0.50.45:6052 · Config: /opt/docker/conf/esphome · Credentials: Vaultwarden esh-docker-vm/esphome-dashboard

Firmware build + OTA dashboard for the ESH ESP32/ESP8266 fleet. As of ESPHome 2026.6.0 the old dashboard is replaced by ESPHome Device Builder 1.0.0, a ground-up rewrite — same function, different UI. Not a fault.

Deploy

scripts/deploy-stack.sh esh-docker-vm esphome
ssh infra-ops@esh-docker-vm 'cd /opt/docker/compose/esphome && sudo -n docker compose up -d'

The .env is never synced in either direction (see the root CLAUDE.md exclusion list). It is created on the host once, from the vault.

Remote build is off

Device Builder 1.0.0 ships remote-build on by default and it binds 0.0.0.0:6055 with mDNS advertisement. Disabled 2026-09-14 via the dashboard's own /ws API — remote_build/set_settings {"enabled": false} — persisted at _remote_build.enabled in /config/.device-builder.json.

⚠ There are two switches and only one closes the port. remote_build/set_offloader_settings {remote_builds_enabled} is the outbound half; setting it false persists, reads back false, and leaves the receiver listening. remote_build/set_settings {enabled} is the receiver-side master switch. The one whose name reads like the master switch is not.

ESPHOME_REMOTE_BUILD_HOST=127.0.0.1 is kept as a backstop: the off state lives in one JSON file whose in-code default is True, so a lost or corrupt settings file re-enables the feature silently.

Three things not to undo

  • privileged: true and network_mode: host are required. USB flashing needs the first, mDNS discovery the second. Removing either breaks device adoption and OTA.
  • Keep the image tag pinned. A bare ghcr.io/esphome/esphome is how this container sat on 2025.8.2 for a year — docker pulled latest once at creation and never again. Every current Everything Presence sensor (Pro, One, Lite) failed esphome config on that build.
  • .esphome/platformio and .esphome/build are excluded from restic (/etc/restic/profiles.yaml). They are 539 MB of regenerable PlatformIO toolchain and build artifacts against 3 KB of actual config; without the exclude, relocating this dir inflates the /opt/docker backup source ~45×. PlatformIO re-downloads them on demand.