Batmon runs fine outside Home Assistant. If you don't need a container, the plain-python setup in Standalone.md is simpler — batmon opens no network ports and has no services to isolate. Use Docker when you already manage your workloads that way.
Prebuilt multi-arch images (linux/amd64, linux/arm64, linux/arm/v7):
ghcr.io/fl4p/batmon-ha:latest # tracks master
ghcr.io/fl4p/batmon-ha:2.05 # pinned release
To build it yourself, pass an Alpine base instead of the Home Assistant one:
git clone https://github.com/fl4p/batmon-ha && cd batmon-ha
docker build --build-arg BUILD_FROM=alpine:3.21 -t batmon-ha .
Copy doc/options.json.template to a directory on the
host and edit it. Every option is the one documented in
README.md; config.yaml holds the authoritative
schema.
mkdir -p ./batmon-data
cp doc/options.json.template ./batmon-data/options.json
Mount the directory, not the file. Batmon writes bms_meter_states.json,
bat_state_<name>.json and user_id next to options.json — those hold your
coulomb-counter and energy-meter totals. If you bind-mount only the file
(-v ./options.json:/data/options.json), /data stays on the container's
filesystem and those totals silently reset every time the container is recreated.
MQTT credentials can also come from the environment — MQTT_HOST, MQTT_PORT,
MQTT_USER, MQTT_PASSWORD. A non-empty value in options.json always wins.
ble_stack selects how batmon talks to Bluetooth, and that decides what the
container needs from the host. This is the whole story:
ble_stack |
network | capabilities | D-Bus | local adapter |
|---|---|---|---|---|
esphome |
bridge (default) | none | none | none — GATT goes over the LAN via ESPHome Bluetooth Proxy |
bleak (default) |
bridge (default) | none | -v /run/dbus:/run/dbus:ro |
host's, via bluetoothd |
bluek |
--net=host |
NET_ADMIN, NET_RAW |
only for pairing | host's, coexists with bluetoothd |
bumble |
--net=host |
NET_ADMIN, NET_RAW |
none | exclusive — brings the adapter down and owns it |
Notes:
bluekandbumbleopenAF_BLUETOOTHHCI/L2CAP sockets, which are bound to the host network namespace. Without--net=hostyou getEAFNOSUPPORT.--net=hostdoes not exist on Docker Desktop for macOS and Windows. On those, useesphome.bumbletakes the adapter away from the host. Give it its own dongle.armv7images ship without theesphomestack — there are no musl/armv7 wheels for its dependencies, so batmon falls back tobleakand logs a warning.
Wired BMSes (type: jk_uart, daly_uart, with address: serial) need the
serial node passed with --device, e.g. --device /dev/ttyUSB0. A bind mount of
the device node is not enough: --device is what installs the cgroup rule that
permits opening it.
The cleanest way to run batmon in a container. Set in options.json:
{
"ble_stack": "esphome",
"bluetooth_proxies": [
{ "host": "192.168.1.42", "name": "garage-proxy" }
],
"devices": [{ "address": "C8:47:8C:E4:55:E5", "type": "jk", "alias": "jk1" }],
"mqtt_broker": "homeassistant.local"
}docker run -d --name batmon --restart unless-stopped \
-v ./batmon-data:/data \
ghcr.io/fl4p/batmon-ha:latest
services:
batmon:
image: ghcr.io/fl4p/batmon-ha:latest
container_name: batmon
restart: unless-stopped
volumes:
- ./batmon-data:/databluek talks to the kernel Bluetooth stack over L2CAP/mgmt sockets. It needs no
D-Bus and leaves bluetoothd running, so the adapter stays usable by the host.
docker run -d --name batmon --restart unless-stopped \
--net=host --cap-add NET_ADMIN --cap-add NET_RAW \
-v ./batmon-data:/data \
ghcr.io/fl4p/batmon-ha:latest
services:
batmon:
image: ghcr.io/fl4p/batmon-ha:latest
container_name: batmon
restart: unless-stopped
network_mode: host
cap_add: [NET_ADMIN, NET_RAW]
volumes:
- ./batmon-data:/data
# wired BMS (type: jk_uart / daly_uart):
# devices:
# - /dev/ttyUSB0:/dev/ttyUSB0For the default bleak stack instead, drop network_mode/cap_add and mount the
host's system bus: -v /run/dbus:/run/dbus:ro. The host must run BlueZ >= 5.43.
Devices with a PIN are paired by a one-shot pre-step before sampling starts.
A Helm chart lives in charts/batmon-ha. It defaults to
ble_stack: esphome, which needs no privileges and no node affinity:
helm install batmon ./charts/batmon-ha -f my-values.yaml
For a local adapter, set hostNetwork: true, add the NET_ADMIN/NET_RAW
capabilities, and pin the pod to the node holding the dongle with nodeSelector.
See the chart's values.yaml.
sashasimkin/batmon-docker is an
independent community chart and image, and was prior art for this one.
ble_stack seems ignored. Older community images built from this repo by
rewriting addon_main.sh's shebang to #!/bin/sh. That silently disabled the
bumble/bluek/esphome stacks — every stack ran as plain bleak. Check the
startup log: batmon prints the stack it selected (BLE stack: ...) as its first
line. Build from entrypoint.sh (the image's CMD) instead of invoking
addon_main.sh directly.
An option seems to have no effect at all. options.json is JSON, so one
missing comma makes the whole file unparseable and everything in it is
ignored. Batmon refuses to start on that, naming the file, the line and the
column and quoting the offending line:
/data/options.json is not valid JSON: Expecting ',' delimiter (line 5, column 3)
"adapter": "hci1"
^
A missing or extra comma between two options is the usual cause.
It never falls back to another configuration file — running settings you did not edit hides the mistake instead of showing it (#414).
No such device / EAFNOSUPPORT with bluek or bumble. Missing
--net=host, or missing NET_ADMIN/NET_RAW.
Permission denied opening a serial port. Use --device, not -v.