# 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 ```bash 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`. 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`. Verified by `ss` (6055 absent) and by the absence of a peer-link "site listening" line on the current boot. ⚠ **The dashboard is still discoverable over mDNS**, and that is a different, milder thing than what was closed. `helpers.dashboard_advertise` announces `_esphomebuilder._tcp.local.` pointing at **6052**, the auth-gated dashboard, independent of remote-build. It leaks existence, address, dashboard id, Device Builder + ESPHome versions and hostname — LAN version fingerprinting, not reachability into a firmware-writing subsystem. Left alone deliberately. ⚠ **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.