Files
Home-Assistant-Watchdog/README.md
T
2026-11-09 00:26:58 +00:00

13 KiB

ha-watchdog

Warning

This project is in development and has not been fully tested.

Status: written but not yet compile-tested. Expect to fix a typo or two on the first build.

It is provided as is, without warranty. You are responsible for any restarts it performs.

A small Go program that watches selected Home Assistant apps (formerly add-ons) and starts them again if they stop. It ships as a single static binary with no dependencies.

Features

  • Lists all installed apps, numbered, with state, version, available updates, and whether each is watched
  • Add and remove apps from a persistent watchlist by number
  • Automatically starts watched apps that are not running
  • Retry cap: gives up on an app after repeated failed starts, so a broken app doesn't loop forever
  • Home Assistant persistent notifications when an app is restarted or the watchdog gives up
  • Skips apps that are unavailable, detached, or not installed
  • Interactive menu or headless -daemon mode
  • Runs on the Home Assistant host itself, or remotely from a separate monitoring machine over SSH
  • Version and build date embedded in the binary

Requirements

  • Home Assistant OS or Supervised (anything with a Supervisor; the ha CLI and Supervisor API are not available in Container or Core installs)
  • For remote monitoring: an SSH server on the Home Assistant host where the ha command works (see Remote monitoring), and the ssh client on the monitoring machine
  • To build: Go 1.21+ (or Docker)

Build

make                    # build for this machine
make arm64              # Raspberry Pi / ARM  -> ha-watchdog-arm64
make amd64              # x86 mini PC / VM    -> ha-watchdog-amd64
make VERSION=1.2.0      # override the version

Not sure which architecture your Home Assistant host uses? Run uname -m on it: aarch64 means arm64, x86_64 means amd64.

Builds are static (CGO_ENABLED=0), so they run on HA OS and Alpine without extra libraries.

No Go installed? Build with Docker:

docker run --rm -v "$PWD":/src -w /src \
  -e CGO_ENABLED=0 -e GOOS=linux -e GOARCH=arm64 \
  golang:1.22 \
  go build -ldflags "-s -w -X main.buildDate=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  -o ha-watchdog-arm64 .

Install on Home Assistant

scp ha-watchdog-arm64 [email protected]:/config/ha-watchdog
ssh [email protected] "chmod +x /config/ha-watchdog && /config/ha-watchdog -version"

This needs the Terminal & SSH app (or host SSH on port 22222). Keeping the binary in /config means it survives reboots.

Usage

Interactive

./ha-watchdog -file /config/watchlist.json
=== HA App Watchdog 1.1.0 | monitor: OFF | 0 watched ===
1) List installed apps
2) Add apps to watchlist
3) Remove apps from watchlist
4) Show watchlist
5) Start/stop monitoring
6) Reset retry counters / give-up state
v) Version info
q) Quit

Typical flow: press 2, enter the numbers of the apps to watch (for example 1,4,7), then press 5 to start monitoring. Monitoring only runs while the program is open in this mode.

Headless

./ha-watchdog -daemon -file /config/watchlist.json

Monitors the saved watchlist until stopped with Ctrl+C or SIGTERM. Build the watchlist first using the interactive menu: if the file is missing, the daemon starts with an empty list and watches nothing. The watchlist is read once at startup, so restart the daemon after changing it.

Flags

Flag Default Description
-daemon off Run headless, no menu
-version Print version and build date, then exit
-interval 30s How often to check app state
-cooldown 60s Minimum time between start attempts for one app
-max-retries 5 Give up on an app after this many start attempts within the retry window
-retry-window 10m Time window for counting start attempts
-file see below Watchlist file path
-ssh Monitor a remote host over SSH, e.g. [email protected]
-ssh-port 22 SSH port on the Home Assistant host
-ssh-key SSH private key file to use
-ha-url Home Assistant URL for remote notifications, e.g. http://homeassistant.local:8123
-ha-token $HA_TOKEN Long-lived access token for remote notifications

Environment variables

Variable Description
SUPERVISOR_TOKEN If set, the Supervisor REST API is used instead of the ha CLI. Provided automatically inside HA apps
WATCHDOG_FILE Watchlist path (overridden by -file)
HA_TOKEN Long-lived access token for remote notifications (overridden by -ha-token)

Watchlist location when -file and WATCHDOG_FILE are not set: /data/watchlist.json if /data exists (inside an HA app), otherwise ./watchlist.json.

How it works

Every -interval, the monitor lists all apps and checks each watched one:

  • Running (started): nothing to do. Any give-up state is cleared.
  • Not running (stopped, error, etc.): sends a start command, subject to the cooldown, and raises a notification.
  • Unavailable, detached, or not installed: skipped, and logged once.
  • Too many failures: after -max-retries starts within -retry-window, the watchdog stops retrying, logs it, and raises a notification. Use menu option 6 to reset, or it clears when the app is seen running again. A flapping app that keeps crashing still trips the cap, because the attempt history is kept when it recovers.

Backends

Condition Backend Notifications
-ssh given ha CLI on the remote host, run over SSH Yes, if -ha-url and -ha-token are set
SUPERVISOR_TOKEN set Supervisor REST API Yes
Otherwise Local ha CLI (ha addons --raw-json, ha addons start) No, logged only

Notifications use the persistent_notification.create service through the Supervisor's Core API proxy. This requires homeassistant_api: true in the app's config.yaml. If notifications don't appear, check the log for a notify: error line.

Remote monitoring

The watchdog can run on a separate machine (a server, NAS, Raspberry Pi, or VM) and manage apps on your Home Assistant host over SSH. This is useful because the monitor keeps working even if Home Assistant itself is struggling. It uses the same watchlist, retry cap, and menu as local mode.

1. Set up SSH access on Home Assistant

Install the Advanced SSH & Web Terminal or Terminal & SSH app and add the monitoring machine's public key to its authorized keys. Then check that this works from the monitoring machine with no password prompt and prints JSON:

ssh [email protected] "ha addons --raw-json"

If you use a non-default port or key, test with -p and -i the same way. The watchdog connects in batch mode, so password prompts will fail rather than hang. If that command doesn't work, the watchdog won't either.

2. (Optional) Enable notifications

In Home Assistant, open your user profile, then Security, and create a long-lived access token. Pass it with the URL:

export HA_TOKEN="your-token"

Without these, remote mode still restarts apps but only logs events.

3. Run it

# Build the watchlist interactively
./ha-watchdog -ssh [email protected] -file ./watchlist.json

# Then run headless
./ha-watchdog -daemon -ssh [email protected] \
  -ssh-key ~/.ssh/ha_watchdog -ha-url http://homeassistant.local:8123 \
  -file ./watchlist.json

Keep it running with systemd

A service has no keyboard, so everything it needs comes from the unit file, an environment file, and files on disk. Build the watchlist interactively first, because -daemon only reads the saved file.

Files involved, and who creates them

File Purpose Created by
watchlist.json Apps to monitor The program, the first time you add or remove an app in the menu. The folder must already exist
/etc/systemd/system/ha-watchdog.service The service definition You
/etc/ha-watchdog/env Holds HA_TOKEN (optional) You
/etc/ha-watchdog/id_ed25519 SSH private key for the HA host You

If the watchlist file is missing when -daemon starts, the program does not fail or create it. It starts with an empty list and watches nothing.

1. Install the binary and set up files

sudo cp ha-watchdog-amd64 /usr/local/bin/ha-watchdog      # match the machine's architecture
sudo mkdir -p /var/lib/ha-watchdog /etc/ha-watchdog
sudo cp watchlist.json /var/lib/ha-watchdog/
sudo cp ~/.ssh/ha_watchdog /etc/ha-watchdog/id_ed25519    # key that can SSH into HA
sudo chmod 600 /etc/ha-watchdog/id_ed25519

2. (Optional) Store the notification token

Keep the token out of the unit file, where anyone can read it with systemctl show:

sudo sh -c 'echo "HA_TOKEN=your-token" > /etc/ha-watchdog/env'
sudo chmod 600 /etc/ha-watchdog/env

3. Accept the SSH host key once

The program connects in batch mode, so it cannot answer the "trust this host?" prompt. Run this once as the same user the service will run as (root by default), type yes, and confirm it prints output:

sudo ssh -i /etc/ha-watchdog/id_ed25519 [email protected] "ha info"

Skip this and every check will fail with a host key error.

4. Create the unit file

# /etc/systemd/system/ha-watchdog.service
[Unit]
Description=Home Assistant app watchdog
After=network-online.target
Wants=network-online.target

[Service]
EnvironmentFile=-/etc/ha-watchdog/env
ExecStart=/usr/local/bin/ha-watchdog -daemon -ssh [email protected] \
  -ssh-key /etc/ha-watchdog/id_ed25519 -ha-url http://homeassistant.local:8123 \
  -file /var/lib/ha-watchdog/watchlist.json
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Where the service gets its input:

  • Command-line flags in ExecStart: -daemon skips the menu, -ssh and -ssh-key say how to reach Home Assistant, and -file points at the watchlist. Add -interval, -max-retries and the other flags here if you want non-default values.
  • EnvironmentFile: supplies HA_TOKEN. The leading - makes the file optional.
  • Files on disk: the watchlist and SSH key.

Optionally add User=hawatchdog to [Service] to avoid running as root. If you do, create that user, make the key and watchlist owned by it, and do step 3 as that user.

5. Enable and check

sudo systemctl daemon-reload
sudo systemctl enable --now ha-watchdog
systemctl status ha-watchdog
journalctl -u ha-watchdog -f

After changing the watchlist, run sudo systemctl restart ha-watchdog, since the daemon reads the file only at startup.

Remote mode caveats

  • The first SSH connection must be accepted by hand as the service user (see step 3 above), or the service cannot connect.
  • If the Home Assistant host is unreachable, each check fails and is logged. The watchdog does not alert on this, since a notification can't be delivered to a host that is down.
  • The SSH key gives the monitor full access to the host's shell, so use a dedicated key and keep it private.
  • Build the binary for the monitoring machine's architecture, not Home Assistant's.

Things to know

  • Apps you stop on purpose will be restarted. Remove an app from the watchlist before stopping it deliberately.
  • Home Assistant has its own per-app Watchdog toggle, which restarts apps that fail a health check. It's worth enabling alongside this, but it only covers apps that define a health check.
  • An app stuck in error for a persistent reason (bad config, missing hardware) won't be fixed by restarts. Check ha addons logs <slug>.
  • The watchdog only ever starts apps. It never stops, updates, or changes them.

Running permanently

Running -daemon over SSH works for testing but stops when the session ends. To run it on the monitoring machine, use the systemd unit above. To run it on Home Assistant itself, package the binary as a custom Home Assistant app (config.yaml with hassio_api: true and homeassistant_api: true, plus a small Dockerfile) so the Supervisor starts it at boot. Inside an app, the watchlist is stored in /data automatically.

Project layout

ha-watchdog/
├── main.go      # the whole program
├── go.mod
├── Makefile     # build targets, injects version and build date
└── README.md

Versioning

The version defaults to the value in main.go and is overridden at build time with -X main.version=.... The build date is injected with -X main.buildDate=...; a plain go build falls back to the git commit time if available, otherwise shows unknown.