ESPHome
Turns YAML into ESP32 firmware and gives Home Assistant a local, encrypted device link
ESPHome compiles a short YAML file into C++ firmware for ESP32, ESP8266, RP2040 and similar microcontrollers, then flashes it over USB once and over the air forever after. It replaces Tuya and Shelly cloud firmware, hand-written Arduino sketches and any device that insists on phoning a vendor to turn on a relay.
Our verdict on ESPHome#
ESPHome is not a server. It is a compiler you open occasionally. The Device Builder dashboard on port 6052 exists to edit YAML and start builds; the product is a .bin sitting on an ESP32 that talks to Home Assistant over the native API on 6053. If the dashboard is down for a month, nothing in your house notices. First flash is over USB, everything after is OTA: the dashboard compiles, uploads to the device's OTA port (3232 on ESP32), the chip writes into its second app partition and reboots into it. Lose Wi-Fi mid-upload and the write is simply incomplete, the bootloader keeps running the old image and you retry. That is the designed path, not a disaster. Version discipline matters more than uptime: 2026.1 removed MD5 OTA authentication in favor of SHA-256, so a dashboard older than 2025.10 cannot update a device already running 2026.1 with a password set.
Who ESPHome is for#
Choose it if
- Anyone flashing cheap ESP32 boards into presence sensors, energy monitors or garage door openers
- People de-clouding Tuya, Sonoff or Shelly hardware that already runs an ESP chip
- Home Assistant users who want a device link that is local, encrypted and needs no MQTT broker
- Anyone who wants twenty identical sensors defined by one shared YAML package
Look elsewhere if
- You want to buy finished devices and never open a text editor
- Your build host is a 1 GB Raspberry Pi, where compiles are slow when they finish at all
- Your devices are Zigbee or Z-Wave, in which case this is the wrong layer entirely
What running it actually looks like#
One container that mostly sleeps. Run ghcr.io/esphome/esphome with your YAML directory mounted at /config and --net=host on Linux, because the dashboard depends on mDNS to see whether devices are online; with -p 6052:6052 instead, only devices with a fixed use_address or manual_ip show status. USB flashing needs --device=/dev/ttyUSB0 and privileged mode, and it does not work at all on macOS containers, where the browser-based web installer is the fallback. The first build for a platform downloads the whole PlatformIO toolchain, so budget ten minutes and a couple of GB before anything compiles. Upgrading ESPHome does not touch your devices; you then have to rebuild and re-push each one.
Resource profile#
- Memory
- 150 MB idle, around 1500 MB in ordinary use. The dashboard idles near 150 MB and does nothing between builds. A compile forks PlatformIO and the toolchain, which routinely wants 1 GB or more, which is why builds die on a 1 GB Pi.
- CPU and acceleration
- Flat until you press Install, then it is a full C++ build across all cores: one to five minutes per device on modern x86, far longer on a Pi. An ESPHome version bump rebuilds every device from scratch.
- Storage growth
- The `.esphome` directory plus the PlatformIO toolchain cache reach a couple of GB after your first ESP32 build, and grow a few hundred MB per device. All of it is regenerable; deleting it costs you one slow rebuild.
- Operational load
- 2 of 5, Light. Nothing runs in steady state and there is no database to corrupt, but monthly releases with breaking changes mean recompiling and re-flashing every device you own, and a bad YAML push puts a device on a shelf you cannot reach.
Figures describe a small single-household install and are the working assumptions behind our stack planner. Your numbers will differ with library size, user count and hardware. See methodology.
Data and backup surface#
Back up the config directory: one YAML per device, any shared packages/ and secrets.yaml. secrets.yaml is the important file, because it holds the API encryption key and the OTA password for every device. Lose it and you cannot reach hardware that is already flashed: Home Assistant refuses the connection and OTA rejects your upload, so you get the ladder and a USB cable. A plain file copy while the dashboard runs is safe, since nothing is a live database. Exclude .esphome/, which is build state and toolchain cache and is fully regenerable. The whole meaningful backup is a few hundred kilobytes of text, so keep it in git with secrets ignored.
Derived backup shape
File copy. Copy the config and data directories. Safe while running for anything that writes atomically.
Traps and surprises#
2026.1 dropped MD5 OTA authentication
OTA now requires SHA-256. If a device is already on 2026.1 or later and uses a password, a dashboard or CLI older than 2025.10 cannot push to it. The fix is ordering: upgrade every machine that installs firmware first, devices second. Get it backwards on a device in a ceiling and your only route back is a USB cable, so upgrade one accessible device first and confirm the OTA path before you touch the rest.
Safe mode is the recovery path, and it has a timer
Push YAML that crashes on boot and the device would loop forever. Safe mode catches this after 10 failed boot attempts, then disables everything except logging, networking and OTA so you can push a fix. A boot only counts as good after 1 minute, and safe mode waits 5 minutes before rebooting again, so you have a narrow window to hit Install. Do not disable safe mode on anything you cannot physically reach.
Every ESPHome upgrade means rebuilding every device
Firmware is compiled against the ESPHome version that built it, so a version bump leaves your fleet on old code until you rebuild and re-flash each device. Twenty devices means twenty compiles and twenty OTAs, and the dashboard will not do it unattended for you. This is the real reason to keep device configs in shared packages: it turns a fleet-wide change from twenty edits into one.
No Wi-Fi fallback means a trip up the ladder
Change your SSID, replace the router, or push a config with a typo in the Wi-Fi block and the device disappears with no way in. Always include an ap: block with captive_portal so a device that cannot join your network raises its own access point and hands you a web form. It costs a little flash space and saves an afternoon on a ladder.
Specifications#
- Category
- Home automation
- Licence
- MIT (Python tooling) and GPL-3.0 (C++ device runtime) (Copyleft)
- Written in
- Python / C++
- First release
- 2018
- Maturity
- Mature
- Datastore
- YAML files on disk, no database
- Default ports
6052,6053,3232- Ships as
- docker, pip package / CLI, home assistant app
- arm64 builds
- Yes
- Identity
- Auth proxy only. The Device Builder dashboard has no user model of its own; as a Home Assistant app it inherits ingress auth, standalone it needs a proxy in front.
- Replaces
- Tuya cloud firmware, Shelly Cloud, Arduino IDE sketches, Sonoff eWeLink
- Project site
- esphome.io
- Source
- github.com/esphome/esphome
- Documentation
- esphome.io/guides/getting_started_hassio.html
Alternatives to ESPHome#
Everything else in home automation, closest in operational weight first.
| Tool | Ops load | Idle RAM | Licence |
|---|---|---|---|
| Node-RED | 2, Light | 110 MB | Apache-2.0 |
| Zigbee2MQTT | 2, Light | 90 MB | GPL-3.0 |
| Frigate | 3, Moderate | 700 MB | MIT |
| Home Assistant | 4, Heavy | 600 MB | Apache-2.0 |
Where ESPHome comes up elsewhere#
Migration guides
Sources#
Facts on this page were checked against the project's own material. Where a number is our own estimate rather than a published figure, the text says so.
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.