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.
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: trueandnetwork_mode: hostare 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/esphomeis how this container sat on 2025.8.2 for a year — docker pulledlatestonce at creation and never again. Every current Everything Presence sensor (Pro, One, Lite) failedesphome configon that build. .esphome/platformioand.esphome/buildare 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/dockerbackup source ~45×. PlatformIO re-downloads them on demand.