Over-the-air updates for your home-made ESP32 fleet

Otter keeps track of your ESP32 and ESP8266 devices and updates their firmware from a single dashboard, with live progress, staged rollouts, release channels and automatic rollback.

A staged rollout to three weather stations: a canary first, with a look at its details page while it updates, then the rest. Real time, recorded with the device simulator.

Everything a small fleet needs

Built for the dozen devices on your shelf, ready for the hundred it may become.

๐Ÿ“ก Live inventory

Every device checks in with its firmware, signal and uptime, and tells what it's made of: exact chip, flash, PSRAM, radios. See who is online, search, filter and tag them.

โšก Updates start in a second

Devices long-poll the server: a deployment reaches them within a second or two, and progress streams live to your browser.

๐Ÿ›Ÿ Safe by default

SHA-256 checks, bootloader rollback on ESP-IDF, downloads that resume after a stall, automatic retries, and images too big for a device's slot refused up front.

๐Ÿชœ Staged rollouts

Release to 10 %, then 50 %, then everyone. Too many failures and the rollout halts before touching the rest of the fleet.

๐Ÿ›ค๏ธ Release channels

Publish on beta or stable and let devices follow a channel: they update themselves, and never downgrade.

๐Ÿ”” Notifications

Failed update, halted rollout, crash, device offline or brand new: get told on ntfy, Discord or any webhook.

๐ŸŽ›๏ธ Commands and configuration

Reboot a device or blink its LED from the dashboard, and change its settings per tag or per device, applied within a second, no reboot.

๐Ÿ’ฅ Crash reports

A device that crashes sends its core dump summary: read the call stack with function names and source lines, per firmware version.

๐Ÿชถ Tiny updates

Devices download a patch from the firmware they run, often a few tens of KB instead of a megabyte, or a compressed image. Stalled downloads resume where they stopped.

๐Ÿ“ถ Wi-Fi without recompiling

One image for every network: set the Wi-Fi over USB right after flashing (Improv, from a browser or a terminal), move a device to a new network from the dashboard.

๐Ÿ”‹ Battery devices

Deep-sleeping devices check in once per wake-up, stay "online" while asleep, and still get updates, commands and settings.

๐Ÿ  Home Assistant & metrics

Devices show up in Home Assistant with a firmware update entity, and a Prometheus endpoint feeds your Grafana dashboards.

๐Ÿณ Runs anywhere

One Docker image for amd64 and arm64 (Raspberry Pi), SQLite inside. HTTPS with Caddy, accounts and API tokens included.

๐Ÿท๏ธ CI releases

Push a tag like weather/v1.6.0: CI builds every hardware variant with that version and uploads it to Otter.

How it works

Devices always call the server, never the other way around: no open port, no NAT trouble, sleepy devices welcome.

Add the agent

Drop the ESP-IDF component or the Arduino library into your firmware and point it at your Otter server.

Devices check in

Each device reports its state and waits for orders. It shows up on the dashboard within seconds.

Upload and deploy

Upload a .bin, deploy it to a device, a tag, a channel or the whole fleet, and watch the progress bars fill.

Hardware support

The protocol is small JSON requests over HTTP(S), documented: other boards and languages can speak it too.

ChipFrameworkOTA + rollbackLong polling
ESP32 family (tested on ESP32-C6, builds for ESP32)ESP-IDF component: HTTPS, device tokens, signatures, crash reports, mDNSโœ“ bootloader rollbackโœ“
ESP32 (tested on ESP32-C6, Arduino core 3)Arduino library: device tokens, signatures, deep sleepโœ“ image validationโœ“
ESP8266Arduino libraryโœ“ image validationperiodic check-ins

Quick start

From nothing to a first over-the-air update in a few minutes.

Start the server

git clone https://github.com/XNinety9/otter
cd otter && docker compose up -d
docker compose exec otter \
  python -m otter.cli create-user admin

Open http://<host>:8000 and log in.

Flash a device

export WIFI_SSID=โ€ฆ WIFI_PASS=โ€ฆ
export OTTER_SERVER=http://<host>:8000
cd firmware/examples/esp32-idf
pio run -e esp32c6 -t upload

It appears on the dashboard a few seconds later.

Ship an update

# token: python -m otter.cli create-token โ€ฆ
export OTTER_TOKEN=otk_โ€ฆ
tools/release.sh otter-demo 1.1.0

It builds and uploads every hardware variant. Click Roll out and watch it update.

No hardware yet? tools/simulate.py spins up a fleet of fake devices that speak the real protocol.

Security, honestly

HTTPS with Caddy, accounts and API tokens, a token per device that can be revoked alone, and signed firmware: devices refuse any image you didn't sign, even from a compromised server.

Over plain HTTP, anyone on your network could read passwords and serve a malicious firmware. Put Otter behind HTTPS before it leaves your desk: the guide shows how, and the protocol's security section says exactly what is protected.