ESPHome OTA Updates
ESPHome’s Over-The-Air (OTA) platform allows you to remotely install modified/updated firmware binaries onto your ESPHome devices over their network interface (Wi-Fi / Ethernet / Thread).
This platform is used by both the ESPHome dashboard as well as the command line interface (CLI) (via
esphome run ... ) to install firmware onto supported devices.
In addition to OTA updates, ESPHome also supports a “safe mode” to help with recovery if/when updates don’t work as expected. This is automatically enabled by this component, but it may be disabled if desired. See Safe Mode for details.
The recommended form reuses the native API encryption key, so one key protects the device; on a device that is already installed, read Encryption before adding the block.
# Example configuration entry, encrypted with the API key (recommended)api: encryption: key: !secret device_name__encryption_key
ota: - platform: esphome encryption:The secret names on this page follow the <hostname>__<base> shape the Device Builder writes to secrets.yaml, see
Using secrets.yaml. Without an API encryption key, a password
authenticates the uploader but does not keep the image confidential:
# Example configuration entry, password only (no API encryption key)ota: - platform: esphome password: !secret device_name__ota_passwordConfiguration variables
Section titled “Configuration variables”-
encryption (Optional): Encrypt the OTA update with the same Noise protocol the native API uses. See Encryption below. Cannot be combined with password.
- key (Optional, string): The base64 encryption key. Omit it so the API encryption key is used and one key protects the device; only set it when there is no static API encryption key, for example a device that uses MQTT without
api:. Next to a static API key it must be the same key, a different one is rejected at validation.
- key (Optional, string): The base64 encryption key. Omit it so the API encryption key is used and one key protects the device; only set it when there is no static API encryption key, for example a device that uses MQTT without
-
password (Optional, string): The password to use for updates. Not recommended for new devices, use encryption instead. Cannot be combined with encryption.
-
allow_partition_access (Optional, boolean): Only on
esp32. Allow updating more partition types. Used for updating the partition table and the bootloader. Defaults tofalse.
IMPORTANT
Prefer encryption over password; it keeps the firmware image confidential in transit, not just authenticated. If you use a password, always use a strong, unique one. See the Security Best Practices guide for more information.
-
port (Optional, int): The port to use for OTA updates. Defaults:
3232for the ESP328266for the ESP82662040for the RP20408892for Beken chips8082for the host platform
-
id (Optional, ID): Manually specify the ID used for code generation.
-
version (Optional, int): Version of OTA protocol to use. Version 2 is more stable. To downgrade to legacy ESPHome, the device should be updated with OTA version 1 first. Defaults to
2. -
All automations supported by Ota.
NOTE
After a serial upload, ESP8266 modules must be reset before OTA updates will work. If you attempt to perform an OTA
update and receive the error message Bad Answer: ERR: ERROR[11]: Invalid bootstrapping, the ESP module/board
must be power-cycled.
Updating the Password
Section titled “Updating the Password”Changing an Existing Password
Section titled “Changing an Existing Password”Since the configured password is used for both compiling and uploading, the regular esphome run <file> command
won’t work. This issue can be worked around by executing the operations separately with an on_boot trigger:
esphome: on_boot: - lambda: |- id(my_ota).set_auth_password("New password");
ota: - platform: esphome id: my_ota password: "Old password"The “id: my_ota” in the OTA block is important. This is referenced in the lambda.
After this trick has been used to change the password, the on_boot trigger may be removed and the old password
replaced with the new password in the ota: section.
Adding a Password
Section titled “Adding a Password”If OTA is already enabled without a password, simply add a password: line to the existing ota: config block. If
the device has an API encryption key, use Encryption instead; a password next to an API key only serves
older uploaders and warns at validation.
Removing a Password
Section titled “Removing a Password”-
If you know your password but want to remove it, enter an empty string:
id(my_ota).set_auth_password("");instead of changing. -
If you no longer know your password and the web server has been activated:
- Remove the OTA password from the configuration
- Build a new image locally.
- Upload it via the
web_serverOTA platform; either through the device’s web interface or by runningesphome upload <config.yaml> --ota-platform web_server(see the Web Server OTA CLI usage section).
Encryption
Section titled “Encryption”Without encryption the OTA update travels over the network in plaintext, and unless a password: is set anyone who
can reach the device can upload to it. The image contains your Wi-Fi credentials and your API encryption key, so anyone
who can capture the traffic on your network can read them. Encrypting the update keeps the image confidential and,
because it uses the Noise NNpsk0 pattern with a pre-shared key, also authenticates the uploader; no password is
needed.
The recommended setup is an API encryption key plus a bare encryption: block under the OTA platform. The OTA uses
the API key, so there is one key per device and nothing else to manage:
api: encryption: key: !secret device_name__encryption_key
ota: - platform: esphome encryption:Only give the OTA block its own key: when there is no static API key to use, for example a device that talks MQTT
and has no api: block, or one whose API key is provisioned at runtime by Home Assistant. Generate a key with the on
demand generator in the API component documentation:
mqtt: broker: !secret mqtt_broker
ota: - platform: esphome encryption: key: !secret device_name__ota_esphome_keyWARNING
A device flashed by serial can carry encryption: from the start. A device updated over the air gets it once it
runs ESPHome 2026.9.0 or newer with an API encryption key, and keeps it from then on. Older firmware cannot offer
encryption, and with the block in the config the CLI refuses to send
plaintext, so an install against it stops with
the device did not offer encryption; refusing to send the image in plaintext. See
Enabling Encryption on an Existing Device for the way around it.
Encryption cannot be combined with a password; the key already authenticates the uploader, so remove the password:
line when you add encryption:. Adding the web server OTA platform next to it is allowed
but warns during validation, since that endpoint accepts the same firmware image over plaintext HTTP. The captive
portal’s update page (captive_portal: without that platform) exists only while the fallback AP is active and is the
intended recovery path for a device that cannot join Wi-Fi, so it does not warn.
IMPORTANT
Once a device runs firmware with encryption: enabled it refuses plaintext OTA updates, and the CLI refuses to send
a plaintext image when encryption: is configured. This is intentional; a downgrade to plaintext would be an
unauthenticated upload. If the key is lost, recover the device with a serial flash, or through the
web server OTA platform if the config has it. Changing the key is not an OTA
operation yet either: the device only accepts the key it is running and the CLI presents the key in the config,
so a new key goes in the same two ways. A
key rotation system is planned for the future.
To troubleshoot a failing encrypted handshake, build with the logger at VERY_VERBOSE so the shared noise
component’s messages are compiled in; a logs: entry alone cannot raise a level above level:. initial_level
keeps every other component at its usual volume:
logger: level: VERY_VERBOSE initial_level: DEBUG logs: noise: VERY_VERBOSEEnabling Encryption on an Existing Device
Section titled “Enabling Encryption on an Existing Device”A device on ESPHome 2026.9.0 or newer with an API encryption key already offers OTA encryption, and esphome run uses
that key whenever the device offers; the encryption: block is what makes the device require it. Add the block, remove
any password:, and install; the encrypted handshake authenticates that upload, so the password is not needed for it.
If the device still runs older firmware, install once without the block first, keeping any existing password: and
with api: encryption: key: set: until ESPHome 2027.3.0 that upload falls back to plaintext with a warning and lands
a firmware that offers, then add the block and drop the password. From 2027.3.0 the CLI no longer falls back to
plaintext when it has a key to use, so a device with an API encryption key still on firmware older than 2026.9.0 must
be updated before then; after that, update it by serial flash or through the
web server OTA platform.
An OTA password: next to an API key only serves older clients and costs about 3.5 KB of flash on an ESP8266, so
validation warns.
A key provisioned at runtime by Home Assistant (an API encryption: block without a key:) makes the device offer
too, but the CLI cannot use a key it does not have, so those uploads stay plaintext until one of these is done:
- Put the key Home Assistant provisioned into
api: encryption: key:. The Device Builder writes it there when Home Assistant hands it over; do not generate a fresh one, that locks Home Assistant out after the next install. - Give the OTA
encryption:block its ownkey:, which leaves the device with two keys: the provisioned API key and the OTA key in the config.
A device without a static API key cannot get its own OTA key: over the air: its running firmware either offers no
encryption at all (an MQTT device) or offers with the provisioned API key rather than the OTA key the CLI would
present, and with the block in the config the CLI does not send plaintext. Put the block in by serial flash or through
the web server OTA platform; after that, installs are encrypted with the OTA key.
Updating the partition table on ESP32
Section titled “Updating the partition table on ESP32”On the ESP32 it is possible to modify the partition table with an OTA update. This feature can be used to update devices that can’t be flashed via serial and have an old partition table with a small NVS partition. It can also be used to convert devices running Tasmota to ESPHome. Before you can update the partition table you need to add the option allow_partition_access: true to the config and install it to your device.
ota: - platform: esphome allow_partition_access: trueCAUTION
There is a risk of bricking the device if the power is interrupted or the ESP is reset during a partition table update, requiring serial flashing to repair it. Make sure you have a stable power supply. The update is usually completed within less than 15 seconds after running the esphome upload command.
IMPORTANT
If the location of the NVS partition changes during a partition table update, the data stored in it won’t be available after the update. In that case, if you used Captive Portal or Improv to configure your WiFi credentials you need to configure them again after updating.
Open the ESPHome logs for detailed information about potential errors. To perform the partition table update you can run the following command:
esphome upload --partition-table name-of-your-config.yamlTo upload a custom partition table from a CSV file, you can run the commands below. gen_esp32part.py is part of ESP-IDF and can be found here.
python3 gen_esp32part.py partitions.csv partitions.binesphome upload --partition-table --file partitions.bin name-of-your-config.yamlCAUTION
When converting a Tasmota ESP32 device to ESPHome, it is required to add allow_partition_access: true on the first ESPHome firmware you upload to the device. Regular OTA updates will not work until the partition table update is completed.
Updating the bootloader on ESP32
Section titled “Updating the bootloader on ESP32”On the ESP32 it is possible to update the bootloader with an OTA update. This is useful on devices that can’t be flashed via serial and need a newer bootloader, for example to enable OTA rollback or SRAM1 as IRAM. Before you can update the bootloader you need to add the option allow_partition_access: true to the config and install it to your device.
ota: - platform: esphome allow_partition_access: trueCAUTION
There is a risk of soft-bricking the device if the power is interrupted or the ESP is reset during the bootloader update, requiring serial flashing to recover it. Make sure you have a stable power supply, and be ready to reflash via USB if it fails.
Open the ESPHome logs for detailed information about potential errors. To perform the bootloader update, run:
esphome upload --bootloader name-of-your-config.yamlWithout --file, the bootloader image from the most recent compile is used (build/bootloader/bootloader.bin for native ESP-IDF, or .pioenvs/<name>/bootloader.bin for PlatformIO).
To upload a custom bootloader binary, run:
esphome upload --bootloader --file bootloader.bin name-of-your-config.yaml