Home Assistant и Docker: сервер умного дома с нуля
Пошаговое руководство по развертыванию Home Assistant в Docker: выбор оборудования, настройка сети, интеграции, мониторинг и резервное копирование для надёжного умного дома.
Введение
Почему Docker для Home Assistant
Если мы посмотрим на варианты запуска Home Assistant, то их четыре: HA OS (ранее HassOS), HA Supervised, HA Container и HA Core. Первые два — это управляемые образы, где система решает за вас, что и как обновлять. Последние два — это чистый Docker-образ и установка в виртуальное окружение Python соответственно.
С практической точки зрения, для домашнего сервера, где уже крутится что-то ещё — тот же Plex, Nextcloud, мониторинг в Prometheus — вариант с Docker Container (официальный образ ghcr.io/home-assistant/home-assistant:stable) даёт максимальную предсказуемость. Вы контролируете версию, знаете, когда и что обновляется, и можете откатиться одной командой, если релиз сломал интеграцию с Zigbee-координатором.
Во-первых, изоляция. Home Assistant в контейнере не тронет системный Python, не потянет за собой systemd-юниты из пакетного менеджера дистрибутива и не конфликтует с библиотеками, которые нужны вашим кастомным скриптам. Во-вторых, воспроизводимость. docker compose up -d на новом железе поднимет идентичное окружение за минуту — достаточно примонтировать том с конфигурацией. В-третьих, сеть. Режим network_mode: host (обязателен для автообнаружения mDNS, UPnP и работы с локальными устройствами вроде Shelly или ESPHome) в Docker работает предсказуемо, в отличие от HA Supervised, где сеть управляет NetworkManager внутри контейнера-супервизора.
Есть нюанс: аддоны (Add-ons) в чистом Docker не работают. Но Mosquitto, Zigbee2MQTT, ESPHome, File Editor, MariaDB, PostgreSQL, Grafana — всё это отдельные контейнеры, которые вы описываете в том же docker-compose.yml. Это не ограничение, а архитектурная гигиена: каждый сервис в своём контейнере, со своими лимитами ресурсов, логами и политикой рестарта.
Сравнение с IKEA уместно: HA OS — это купленная готовая кухня, HA Supervised — кухня-гарнитур, которую собрали за вас, а Docker Compose — это набор модулей, из которых вы собираете именно то, что нужно, с теми фасадами и петлями, которые выбрали сами. Свои нюансы есть, но результат — ваш.
Что понадобится
Железо. Home Assistant не требователен к CPU, но чувствителен к задержкам диска и объёму RAM при параллельной работе базы данных, логгера и интеграций. Минимум для комфортной работы с 50+ устройствами, историей за год и парой камер через Frigate:
- CPU: 2 vCPU (x86_64 или ARM64). Raspberry Pi 4/5 — работает, но на Pi 4 с 4 ГБ RAM уже тесно, если поднимаете PostgreSQL и Frigate локально. Бывший в употреблении Thin Client (Dell Wyse 5070, HP t640) или Mini PC (Beelink, Minisforum) на N100/N305 — оптимальное соотношение ватт/производительность/цена на 2026 год.
- RAM: 4 ГБ — абсолютный минимум, 8 ГБ — комфортно, 16 ГБ — с запасом на Frigate, PostgreSQL, Grafana Loki и будущие эксперименты.
- Диск: 32 ГБ eMMC/SD — для системы, но базу данных (
home-assistant_v2.db) и медиа лучше вынести на отдельный SSD/NVMe. SD-карты умирают предсказуемо — обычно в самый неподходящий момент. ZFS-пул с сжатиемzstdиatime=offна отдельном накопителе решит вопрос надёжности раз и навсегда. - Сеть: проводной Ethernet 1 Гбит/с. Wi-Fi для сервера умного дома — плохая идея: mDNS, SSDP и Zigbee2MQTT/Thread Border Router требуют стабильного L2-сегмента.
Программная база. Предполагаю, что на хосте уже стоит Debian 12 (bookworm) / Ubuntu 24.04 LTS / Alpine Linux и настроен Docker Engine + Docker Compose v2 (плагин docker compose, а не устаревший docker-compose на Python). Проверьте:
bashdocker version --format '{{.Server.Version}}'
docker compose version
Ожидаемый вывод — Docker Engine 27.x и Compose v2.29+. Если Compose ставился через apt install docker-compose-plugin — всё верно.
Сеть. Зафиксируйте статический IP для хоста (через DHCP reservation на роутере или systemd-networkd / Netplan на самом хосте). Home Assistant должен быть доступен по предсказуемому адресу, например 192.168.10.10. DNS-имя ha.local (или homeassistant.local — дефолт mDNS) настроится автоматически при включении network_mode: host, но для обратного прокси и TLS удобнее иметь запись в локальном DNS (AdGuard Home, Pi-hole, Unbound) или в /etc/hosts клиентов.
Порты. На хосте не должно висеть ничего на 8123/tcp (веб-интерфейс HA), 5353/udp (mDNS), 1900/udp (SSDP). Проверьте:
bashss -tulpn | grep -E ':8123|:5353|:1900'
Если порт занят — разберитесь, чем, перед деплоем.
Резервное копирование. Планируйте стратегию до первого запуска. Минимум: ежедневный снапшот конфигурации (/config) на отдельный носитель/сетевую шара. Идеально: бэкап в S3-совместимое хранилище (MinIO на TrueNAS, Backblaze B2, R2) через restic или borgbackup с шифрованием и проверкой целостности. Про «как настроить бэкап правильно» — отдельная глава, но закладывайте том /backup в compose-файл сразу.
Структура каталогов на хосте (пример для /opt/ha):
text/opt/ha/ ├── docker-compose.yml ├── .env # переменные окружения: TZ, PUID, PGID, версии образов ├── config/ # том конфигурации HA (configuration.yaml, automations, .storage) ├── addons/ # конфиги аддонов: mosquitto, zigbee2mqtt, esphome, etc. ├── backup/ # локальные снапшоты перед выгрузкой в S3 ├── media/ # медиа-файлы камер, TTS, звуки уведомлений └── ssl/ # сертификаты для reverse proxy (если терминируете TLS на хосте)
Права доступа. Контейнер Home Assistant запускается от пользователя с UID/GID 1000 (дефолт в образе). Если на хосте ваш пользователь — тоже 1000, проблем нет. Если нет — задайте PUID=1000 PGID=1000 в .env и убедитесь, что каталоги config/, addons/, backup/, media/ принадлежат этому UID:GID. Иначе HA не сможет писать в .storage, обновлять интеграции и сохранять бэкапы.
bash# пример подготовки на хосте
sudo mkdir -p /opt/ha/{config,addons,backup,media,ssl}
sudo chown -R 1000:1000 /opt/ha
Всё, база готова. Дальше — docker-compose.yml, настройка configuration.yaml, интеграция Zigbee/Thread/Matter, мониторинг и алертинг.
Развёртывание Home Assistant в Docker
Создание docker-compose.yml
Начнём с главного: Home Assistant в Docker запускается через официальный образ ghcr.io/home-assistant/home-assistant:stable. Не тяните latest — в продакшене (даже домашнем) важна предсказуемость. Тег stable обновляется раз в две недели, после выхода патч-релиза. Если хотите жить на острие — есть beta и dev, но за ломаные интеграции отвечаете сами.
Минимальный рабочий docker-compose.yml дляamd64/arm64 выглядит так:
yamlversion: '3.8'
services:
homeassistant:
image: ghcr.io/home-assistant/home-assistant:stable
container_name: homeassistant
restart: unless-stopped
network_mode: host
volumes:
- /opt/homeassistant/config:/config
- /etc/localtime:/etc/localtime:ro
- /run/dbus:/run/dbus:ro
environment:
- TZ=Europe/Moscow
# healthcheck нужен, чтобы Docker знал: жив ли контейнер
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8123/"]
interval: 30s
timeout: 10s
retries: 5
start_period: 40s
Разберём по пунктам.
network_mode: host — это не опционально. Home Assistant должен видеть локальную сеть «как есть»: mDNS, SSDP, broadcast-рассылки от Zigbee-шлюзов, Bluetooth-прокси, ESPHome-устройств. В bridge-сети это работает через дополнительные костыли (avahi-reflector, macvlan), которые ломаются при обновлении ядра. Host-сеть убирает класс проблем целиком. Порт 8123 будет занят на хосте — учитывайте это, если на той же машине висит Nginx или Caddy.
/run/dbus:/run/dbus:ro — нужен для работы с Bluetooth (BlueZ), ModemManager, NetworkManager. Без этого не поднимете Bluetooth-прокси для ESPHome или не заведёте Zigbee-стик через ZHA, если он требует доступ к системной шине.
Healthcheck — не декор. Если контейнер завис в состоянии «запускается» (часто бывает после некорректного обновления или поломки БД), Docker не перезапустит его без healthcheck. start_period: 40s даёт время на первую инициализацию и миграции БД.
Положите файл в /opt/homeassistant/docker-compose.yml. Директорию для конфига создайте заранее:
bashmkdir -p /opt/homeassistant/config
chown -R 1000:1000 /opt/homeassistant/config
UID/GID 1000 — дефолт внутри контейнера. Если на хосте другой пользователь — подставьте свои. Запуск:
bashcd /opt/homeassistant
docker compose up -d
docker compose logs -f
Первый запуск тянет образ (~400 МБ), создаёт структуру конфига, генерирует configuration.yaml и поднимает веб-интерфейс на http://<IP-хоста>:8123. Настройка через UI — создание владельца, выбор имени дома, часового пояса — занимает пару минут.
Настройка volumes и сетей
С базовым запуском разобрались. Теперь — про то, как не потерять данные при обновлении контейнера и как выставить наружу только то, что нужно.
Volumes: разделяйте конфиг и данные
Официальный образ кладёт всё в /config. Но внутри там смешаны: YAML-конфиги, база данных SQLite (home-assistant_v2.db), логи, кэш интеграций, SSL-сертификаты, медиа-файлы. При бэкапе и миграции удобнее, когда это разделено.
Мой вариант для продакшена:
yamlvolumes:
# Конфигурация — читается часто, пишется редко
- /opt/homeassistant/config:/config:rw
# База данных — интенсивный I/O, выносим на отдельный том/диск
- ha_db:/config/home-assistant_v2.db
# Медиа и бэкапы — большой объём, не нужен на быстром SSD
- /mnt/storage/homeassistant/media:/config/media:rw
- /mnt/storage/homeassistant/backups:/config/backup:rw
# SSL — если терминируете TLS внутри HA (не рекомендую, см. ниже)
- /opt/homeassistant/ssl:/config/ssl:ro
Создайте именованный том для БД:
bashdocker volume create ha_db
Почему так: SQLite на ZFS/btrfs с sync=always или на HDD даёт зависания UI на запись. Вынося БД в отдельный том, можете положить его на NVMe с sync=disabled (риск потери последних секунд при отключении питания — приемлем для телеметрии), а медиа и бэкапы — на медленный RAID. Конфиг остаётся на системном диске — он мелкий, читается при старте, пишется только при правке через UI.
Сеть: не пробрасывайте 8123 наружу напрямую
network_mode: host удобен внутри LAN, но если вы пробросите порт 8123 на роутере — получите брутфорс за сутки. Правильный путь: reverse proxy на том же хосте (Caddy, Nginx, Traefik) + TLS + базовая авторизация или Authelia/Keycloak.
Пример для Caddy (ставится отдельно, тоже в Docker):
caddy# /opt/caddy/Caddyfile ha.example.com { reverse_proxy 127.0.0.1:8123 { header_up X-Forwarded-Proto https header_up X-Forwarded-Host {host} } # Базовая защита от ботов @bots { header User-Agent *bot* header User-Agent *crawler* header User-Agent *spider* } respond @bots 403 }
В docker-compose.yml Home Assistant добавляем зависимость от Caddy (чтобы стартовал после) и убираем порт из хоста — он и так на 127.0.0.1:8123 доступен только локально:
yamlservices:
homeassistant:
# ... остальное без изменений
depends_on:
caddy:
condition: service_healthy
Caddy сам выдаст и продлит Let's Encrypt сертификаты. Никаких certbot-кронов, никаких ручных действий.
Дополнительные сети для изоляции
Если у вас несколько контейнеров (Mosquitto, Zigbee2MQTT, Frigate, PostgreSQL для recorder), не сваливайте всё в host-сеть. Создайте пользовательскую bridge-сеть:
yamlnetworks:
ha_internal:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/24
И подключайте к ней сопутствующие сервисы. Home Assistant в network_mode: host всё равно видит их по 172.28.0.x, а они не видят друг друга без необходимости. Mosquitto не должен быть доступен из интернета — ни в коем случае.
Пример для Mosquitto в той же compose-стеке:
yaml mosquitto:
image: eclipse-mosquitto:2
container_name: mosquitto
restart: unless-stopped
networks:
- ha_internal
volumes:
- /opt/mosquitto/config:/mosquitto/config:ro
- /opt/mosquitto/data:/mosquitto/data
- /opt/mosquitto/log:/mosquitto/log
healthcheck:
test: ["CMD", "mosquitto_sub", "-t", "$$SYS/broker/uptime", "-C", "1", "-i", "healthcheck"]
interval: 30s
timeout: 10s
retries: 3
В Home Assistant (Settings → Devices & Services → MQTT) указываете хост 172.28.0.2 (или имя сервиса mosquitto, если HA тоже в этой сети — но у нас host mode, поэтому IP).
Итог: рабочий docker-compose.yml — это не про «запустилось», а про «переживёт обновление, падение питания и переезд на другое железо». Вынесли БД в отдельный том, закрыли веб-морду за reverse proxy, изолировали MQTT и периферию в свою сеть. Дальше — интеграции, но это уже тема для следующего раздела.
Интеграции и периферия
Zigbee2MQTT и Bluetooth
С практической точки зрения, Zigbee — это единственный протокол, который в домашних условиях даёт предсказуемость. Wi-Fi датчики съедают адреса в DHCP, зависают при перезагрузке роутера и требуют отдельного VLAN, если вы параноик. Z-Wave — дорог и умирает. Thread/Matter — перспективно, но в 2026 году экосистема ещё не зрела для «поставил и забыл».
Zigbee2MQTT запускается как отдельный контейнер рядом с Home Assistant. Не ставьте его аддоном внутри HA — при обновлении ядра вы потеряете управление сетью, а пересборка координатора — удовольствие на час.
Железо. Берём Sonoff Zigbee 3.0 USB Dongle Plus (на чипе EFR32MG21) или Home Assistant SkyConnect. Оба прошиваются официальными прошивками, оба поддерживают Zigbee 3.0 и Thread. У Sonoff лучше антенна, у SkyConnect — нативная поддержка в HA. Я держу Sonoff на винни-пухе (мой основной Proxmox-хост), SkyConnect — в резерве.
Подключение через USB-проброс в Docker Compose:
yamlservices:
zigbee2mqtt:
image: koenkk/zigbee2mqtt:2.3.0
container_name: zigbee2mqtt
restart: unless-stopped
network_mode: host
volumes:
- /opt/zigbee2mqtt/data:/app/data
- /run/udev:/run/udev:ro
devices:
- /dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_00d4b1faa612ec11879723c7bd930c07-if00-port0:/dev/ttyUSB0
environment:
- TZ=Europe/Moscow
# healthcheck важен: если координатор завис, рестартим контейнер
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Важно: используйте /dev/serial/by-id/..., а не /dev/ttyUSB0. При перезагрузке хоста нумерация ttyUSB* может прыгать, by-id — стабилен. Узнать путь:
bashls -la /dev/serial/by-id/
Конфигурация /opt/zigbee2mqtt/data/configuration.yaml — минимально необходимая:
yamlhomeassistant: true
permit_join: false
mqtt:
base_topic: zigbee2mqtt
server: mqtt://mqtt:1883
user: zigbee
password: ${MQTT_ZIGBEE_PASSWORD}
serial:
port: /dev/ttyUSB0
adapter: ezsp
advanced:
network_key: GENERATE_YOUR_OWN_32_HEX_CHARS
pan_id: 0x1a62
channel: 15
# канал 15 не пересекается с Wi-Fi 1, 6, 11
# pan_id фиксируем — при пересоздании контейнера сеть не развалится
frontend:
port: 8080
auth_token: ${Z2M_FRONTEND_TOKEN}
experimental:
output: json
Сгенерируйте network_key один раз: openssl rand -hex 16. Сохраните в 1Password / Bitwarden / keepass. Потеря ключа = пересборка всей Zigbee-сети с нуля.
MQTT-брокер — отдельный контейнер (Mosquitto или EMQX). Не используйте встроенный в HA — он не умеет ACL и персистентность на уровне, нужном для продакшена. У меня mqtt — это отдельный сервис в том же docker-compose, с данными на ZFS-датасете.
Bluetooth. Для Xiaomi/Aqara кнопок, датчиков температуры LYWSD03MMC, смарт-замков — нужен Bluetooth-прокси. В 2026 году два варианта:
- ESPHome Bluetooth Proxy на ESP32-C3 (например, ESP32-C3-DevKitM-1). Стоит ~400 ₽, прошивается за 5 минут, работает по Wi-Fi, отдаёт рекламные пакеты в HA через ESPHome API.
- USB Bluetooth 5.0+ донгл (CSR8510 или на базе Realtek RTL8761B) — воткнут прямо в хост, проброшен в контейнер
bluetooth(официальный образhomeassistant/bluetooth).
Второй вариант проще, если сервер стоит в центре квартиры. Первый — если нужно покрыть дальние комнаты. Я использую оба: донгл на сервере для ближней зоны, три ESP32-C3 — в спальне, на кухне, в ванной.
docker-compose.yml для Bluetooth-донгла:
yaml bluetooth:
image: homeassistant/bluetooth:2024.12.0
container_name: bluetooth
restart: unless-stopped
network_mode: host
privileged: true
devices:
- /dev/bus/usb:/dev/bus/usb
volumes:
- /run/dbus:/run/dbus
environment:
- DBUS_SYSTEM_BUS_ADDRESS=unix:path=/run/dbus/system_bus_socket
В Home Assistant: Настройки → Устройства и службы → Bluetooth → «Настроить» → выбираем bluetooth контейнер. После этого HA начинает видеть BLE-объявления.
Подключение датчиков и устройств
Во-первых, парьтесь. В Zigbee2MQTT веб-интерфейс (порт 8080) — кнопка «Permit join (all)». Нажимаете, держите 3 минуты, вставляете батарейку в датчик / зажимаете кнопку на 5 секунд — устройство появляется в списке. Переименовываете сразу: 0x00158d000xxxxxxx → sensor_temperature_bedroom. Не ленитесь — через полгода не вспомните, что за 0x00158d0002a1b3c4.
Пример автоматизации переименования через MQTT (если устройств десятки):
bash# Получить список
mosquitto_sub -h mqtt -u zigbee -P "$MQTT_ZIGBEE_PASSWORD" -t 'zigbee2mqtt/bridge/devices' -C 1 | jq .
В Home Assistant устройства появляются автоматически благодаря homeassistant: true в конфиге Z2M. Но — важный нюанс — сущности создаются с техническими именами. Исправляйте в UI: Настройки → Устройства и службы → Zigbee2MQTT → Устройство → сущность → шестерёнка → «Имя сущности». Делайте это сразу, иначе в YAML-автоматизациях будете писать sensor.temperature_23_45 вместо sensor.temperature_bedroom.
Типовые проблемы и решения из практики:
| Симптом | Причина | Решение |
|---|---|---|
| Датчик не парьется | Канал 15 занят Wi-Fi | Смените канал на 20 или 25 в configuration.yaml, перезапустите Z2M, репарьте |
| Устройство «unavailable» через день | Слабый сигнал / нет репитеров | Добавьте розетку с питанием (Sonoff S26, IKEA Tradfri) — они работают как роутеры |
| Кнопка Aqara двойное нажатие не работает | Не включен legacy_action_triggers | В настройках устройства в Z2M UI: legacy_action_triggers: true |
| Bluetooth датчик теряется | Донгл забит другими устройствами | bluetoothctl → power off → power on в cron каждые 6 часов, или ставьте ESPHome proxy |
Репитеры. Любое Zigbee-устройство с постоянным питанием (розетка, реле, лампочка) — репитер. Планируйте размещение: максимум 10–15 метров между узлами через стены. У меня в каждой комнате минимум одна розетка Sonoff S26R2ZB — дешёвы, компактны, не глючат.
Бэкап Zigbee-сети. Файл /opt/zigbee2mqtt/data/coordinator_backup.json создаётся автоматически при грамотной прошивке координатора (Sonoff/SkyConnect поддерживают). Настройте его копирование в ваш бэкап-скрипт (Borg/Restic на TrueNAS). Восстановление: останавливаете Z2M, кладёте файл на место, запускаете — сеть поднимается без репайринга всех устройств.
Файрвол. Если у вас разные VLAN для IoT и управления — пробросьте порт 1883 (MQTT) и 8080 (Z2M UI) только от хоста с HA. Не открывайте наружу. Zigbee2MQTT не имеет встроенной аутентификации веб-интерфейса — auth_token в конфиге защищает только API, не UI.
С Bluetooth проще: после настройки прокси/донгла устройства обнаруживаются в HA автоматически. Добавляете через UI: Настройки → Устройства и службы → Bluetooth → «Добавить устройство». Имя даёте человеческое сразу.
Итог: Zigbee2MQTT в Docker + MQTT-брокер + правильный канал/ключ/репитеры — фундамент, который работает годами. Bluetooth — дополнение для того, что не зашло в Zigbee (Xiaomi, смарт-замки). Не смешивайте протоколы без нужды: каждый дополнительный радиомодуль — точка отказа и поверхность атаки.
Мониторинг и резервное копирование
Настройка Prometheus и Grafana
Home Assistant «из коробки» отдаёт метрики в формате Prometheus по эндпоинту /api/prometheus. Доступ к нему защищён long-lived токеном — это удобнее, чем возиться с basic auth или отдельным sidecar-экспортером.
Создаём токен: профиль → Длинноожидающие токены доступа → «Создать токен». Назовём его prometheus-scraper, срок действия — неограниченный (или год, если политика требует ротации). Токен скопируйте сразу — второй раз не покажет.
Теперь конфиг Prometheus. Добавляем джобу в prometheus.yml:
yamlscrape_configs:
- job_name: 'home-assistant'
static_configs:
- targets: ['homeassistant:8123']
metrics_path: '/api/prometheus'
scheme: http
authorization:
credentials: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...' # ваш токен
scrape_interval: 30s
scrape_timeout: 10s
Обратите внимание: homeassistant — это имя сервиса в той же Docker-сети, что и Prometheus. Если Prometheus крутится на отдельной ноде — подставьте IP или DNS-имя ноды с HA.
Проверяем: curl -H "Authorization: Bearer <token>" http://homeassistant:8123/api/prometheus | head -20. Должен вернуть текст с метриками вроде python_info{version="3.11.9"} 1.0.
Что реально полезно мониторить:
hass_entities_total— общее число сущностей. Резкий скачок = кто-то подключил интеграцию без спроса или сломался MQTT-брокер.hass_state_changes_total— частота смены состояний. Аномальный рост = зацикленная автоматизация или глючный датчик.hass_automation_triggered_total— счетчик срабатываний автоматизаций. Полезно для алерта «автоматика не сработала за 24 часа».process_resident_memory_bytes/process_cpu_seconds_total— ресурсы контейнера. HA любит память, особенно с большим количеством интеграций.
Графану не буду учить настраивать — импорт дашборда делается за минуту. Рекомендую дашборд Home Assistant Overview (ID 10734 на grafana.com) — он уже разбит по панелям: сущности, автоматизации, скрипты, системные метрики. Добавляете datasource Prometheus, импортируете JSON — готово.
Алерты кладите в alerts.yml и подключаете через rule_files в prometheus.yml. Минимальный набор:
yamlgroups:
- name: home-assistant
rules:
- alert: HomeAssistantDown
expr: up{job="home-assistant"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Home Assistant недоступен"
description: "Скрапинг фейлится уже 2 минуты"
- alert: HomeAssistantHighMemory
expr: (process_resident_memory_bytes{job="home-assistant"} / 1024 / 1024) > 2048
for: 10m
labels:
severity: warning
annotations:
summary: "HA потребляет более 2 ГБ RAM"
description: "Текущее потребление: {{ $value }} MB. Проверьте интеграции."
- alert: HomeAssistantNoStateChanges
expr: rate(hass_state_changes_total[15m]) == 0
for: 30m
labels:
severity: warning
annotations:
summary: "Нет смены состояний 30 минут"
description: "Возможно, HA завис или отвалился MQTT/Zigbee координатор"
Порог 2 ГБ под мою конфигурацию (около 150 сущностей, Zigbee2MQTT, Frigate, несколько камер). Подбирайте под себя — top внутри контейнера покажет реальную картину.
Автоматизация бэкапов
Резервное копирование в HA — тема, где «сделал и забыл» приводит к потере истории за год. Официальный аддон «Backup» работает только в supervised-установке. В Docker мы собираем бэкап сами: конфиг (/config), медиа (/media), SSL-сертификаты (если терминируете TLS на HA) и дамп БД (если используете внешний PostgreSQL — отдельно, через pg_dump).
Скрипт бэкапа кладу в /opt/scripts/ha-backup.sh на хосте (не в контейнере — хост имеет доступ к томам и к удалённому хранилищу):
bash#!/usr/bin/env bash
set -euo pipefail
# === Конфигурация ===
HA_CONFIG_DIR="/srv/homeassistant/config"
HA_MEDIA_DIR="/srv/homeassistant/media"
BACKUP_ROOT="/srv/backups/homeassistant"
RETENTION_DAYS=30
RCLONE_REMOTE="nas:backups/homeassistant" # настроенный rclone remote
DATE=$(date +%Y-%m-%d_%H-%M-%S)
BACKUP_NAME="ha-backup-${DATE}.tar.gz"
TMP_DIR=$(mktemp -d)
# === Логирование ===
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
log "Начинаю бэкап Home Assistant"
# Останавливаем HA для консистентного снапшота БД (SQLite)
log "Останавливаю контейнер homeassistant..."
docker stop homeassistant
# Архивируем
log "Создаю архив..."
tar -czf "${TMP_DIR}/${BACKUP_NAME}" \
-C "${HA_CONFIG_DIR}" . \
-C "${HA_MEDIA_DIR}" . 2>/dev/null
# Запускаем HA обратно
log "Запускаю контейнер homeassistant..."
docker start homeassistant
# Перемещаем в локальное хранилище
mv "${TMP_DIR}/${BACKUP_NAME}" "${BACKUP_ROOT}/"
log "Локальный бэкап сохранён: ${BACKUP_ROOT}/${BACKUP_NAME}"
# Загружаем на NAS/облако через rclone
if command -v rclone >/dev/null 2>&1; then
log "Синхронизирую с удалённым хранилищем..."
rclone copy "${BACKUP_ROOT}/${BACKUP_NAME}" "${RCLONE_REMOTE}" --progress
log "Удалённый бэкап завершён"
else
log "rclone не найден, пропускаю удалённую копию"
fi
# Ротация локальных бэкапов
log "Удаляю бэкапы старше ${RETENTION_DAYS} дней..."
find "${BACKUP_ROOT}" -name 'ha-backup-*.tar.gz' -mtime +${RETENTION_DAYS} -delete
# Ротация на удалёнке (rclone delete с --min-age)
if command -v rclone >/dev/null 2>&1; then
rclone delete "${RCLONE_REMOTE}" --min-age ${RETENTION_DAYS}d --include 'ha-backup-*.tar.gz'
fi
log "Бэкап завершён успешно"
rm -rf "${TMP_DIR}"
Делаем исполняемым: chmod +x /opt/scripts/ha-backup.sh.
Почему останавливаю контейнер: SQLite не любит горячие копии. tar читает файлы последовательно — если HA в этот момент пишет в БД, получите коррупцию. Остановка на 10–15 секунд приемлема для домашнего сервера. Если нужен нулевой даунтайм — мигрируйте на PostgreSQL и делайте pg_dump без остановки.
Systemd-таймер для запуска каждую ночь в 03:15:
/etc/systemd/system/ha-backup.service:
ini[Unit]
Description=Home Assistant Backup
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
ExecStart=/opt/scripts/ha-backup.sh
StandardOutput=journal
StandardError=journal
/etc/systemd/system/ha-backup.timer:
ini[Unit]
Description=Daily Home Assistant Backup at 03:15
[Timer]
OnCalendar=*-*-* 03:15:00
Persistent=true
RandomizedDelaySec=15m
[Install]
WantedBy=timers.target
Включаем: systemctl daemon-reload && systemctl enable --now ha-backup.timer.
Проверка: systemctl list-timers ha-backup.timer покажет следующее срабатывание. Логи — journalctl -u ha-backup.service -f.
Восстановление — отдельный скрипт, который я держу рядом (ha-restore.sh). Суть: останавливаем HA, распаковываем архив в /srv/homeassistant/config и /srv/homeassistant/media, фиксим владельца (chown -R 1000:1000 — UID/GID пользователя внутри контейнера), запускаем HA. Тестируйте восстановление раз в квартал. Бэкап, который не восстанавливается — не бэкап, а файл на диске.
Итог: мониторинг даёт видимость до того, как пользователи (жена, дети) начнут жаловаться «умный дом не работает». Бэкапы — страховка от потери лет истории и настроек. Оба механизма должны быть скучными, предсказуемыми и проверяемыми. Если вы не получали алерт «Home Assistant Down» за полгода — проверьте, работает ли Alertmanager.
Часто задаваемые вопросы
Как обновлять Home Assistant?
Обновление в Docker — это стандартный цикл pull → stop → remove → start. Никакой магии, просто контейнер с новым образом.
Ручной способ (рекомендую):
bashcd /opt/homeassistant
docker compose pull
docker compose up -d --remove-orphans
Флаг --remove-orphans чистит старые контейнеры, если вы меняли имя сервиса в compose-файле. После запуска проверяйте логи:
bashdocker compose logs -f homeassistant | head -50
Ищите строку Home Assistant initialized — значит, всё подошло.
Перед обновлением — всегда бэкап конфигурации. Минимум:
bashtar -czf /backup/ha-config-$(date +%F).tar.gz -C /opt/homeassistant/config .
Релизы выходят каждые три недели (обычно в среду). Читайте changelog — иногда ломают интеграции или меняют конфиг-схему. Если видите BREAKING CHANGES для своих интеграций — читайте миграционный гайд перед pull.
Автообновления через Watchtower — вариант для ленивых, но я не фанат. Home Assistant иногда требует ручных действий после мажорного релиза (миграция БД, правка YAML). Watchtower обновит контейнер в 3 ночи, а утром вы получите неработающий умный дом.
Если очень хочется автоматику — настройте Watchtower только на уведомления, а применение делайте руками:
yaml# docker-compose.watchtower.yml
services:
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_MONITOR_ONLY=true
- WATCHTOWER_SCHEDULE=0 0 6 * * *
- WATCHTOWER_NOTIFICATIONS=shoutrrr
- WATCHTOWER_NOTIFICATION_URL=telegram://<bot_token>@<chat_id>
Тогда в 6 утра придёт сообщение «есть обновление», а решите вы — ставить или подождать.
Где хранить конфигурацию?
Единственно правильный ответ: в bind-mount на хосте, под версионным контролем.
В docker-compose.yml:
yamlservices:
homeassistant:
image: ghcr.io/home-assistant/home-assistant:stable
container_name: homeassistant
volumes:
- /opt/homeassistant/config:/config
- /etc/localtime:/etc/localtime:ro
# ...
Путь /opt/homeassistant/config на хосте — это ваша единственная истина. Внутри контейнера это /config, но снаружи вы видите живые файлы.
Почему не named volume? Потому что docker volume inspect и docker cp — это лишние абстракции. С bind-mount вы:
- Редактируете
configuration.yamlв любимом редакторе на хосте - Видите изменения сразу в
git diff - Бэкапите одной командой
tarилиrsync - Переносите на другой сервер
scp -r
Структура, которую я использую:
/opt/homeassistant/
├── config/
│ ├── configuration.yaml
│ ├── secrets.yaml
│ ├── automations.yaml
│ ├── scripts.yaml
│ ├── scenes.yaml
│ ├── dashboards/
│ │ └── lovelace.yaml
│ ├── packages/
│ │ ├── climate/
│ │ ├── lighting/
│ │ └── security/
│ └── custom_components/ # только если форкаете интеграции
├── docker-compose.yml
├── .env # токены, пароли — НЕ в git
└── backup/ # локальные снапшоты
Git-воркфлоу:
bashcd /opt/homeassistant
git init
echo ".env" > .gitignore
echo "config/.storage/" >> .gitignore # внутреннее состояние HA
echo "config/home-assistant.log" >> .gitignore
echo "config/home-assistant_v2.db*" >> .gitignore
git add .
git commit -m "Initial HA config"
После каждого изменения конфига — коммит. Перед обновлением — тег:
bashgit tag -a pre-update-2026.09 -m "Before 2026.9 update"
Если обновление сломало что-то — git checkout pre-update-2026.09 и вы за минуту в рабочем состоянии.
Секреты — только в secrets.yaml (который в .gitignore) или в .env файле compose. Никаких паролей в configuration.yaml. Пример:
yaml# configuration.yaml
mqtt:
broker: !secret mqtt_broker
username: !secret mqtt_user
password: !secret mqtt_pass
yaml# secrets.yaml (не в git!)
mqtt_broker: "192.168.10.5"
mqtt_user: "ha_mqtt"
mqtt_pass: "сгенерированный_длинный_пароль"
А в .env для compose:
bash# .env
POSTGRES_PASSWORD=тоже_сложный_пароль
GRAFANA_ADMIN_PASSWORD=и_этот
Бэкап стратегия — 3-2-1:
- Локально:
/opt/homeassistant/backup/(каждый день по крону) - На сторадж (TrueNAS/NFS):
rsyncраз в час - В облако (S3/B2/Rclone): раз в сутки, зашифрованный
resticилиborg
Скрипт для крона (/etc/cron.daily/ha-backup):
bash#!/bin/bash
set -euo pipefail
CONFIG_DIR="/opt/homeassistant/config"
BACKUP_DIR="/opt/homeassistant/backup"
RETENTION_DAYS=30
DATE=$(date +%F-%H%M)
ARCHIVE="${BACKUP_DIR}/ha-config-${DATE}.tar.gz"
tar -czf "${ARCHIVE}" -C "${CONFIG_DIR}" . --exclude='home-assistant_v2.db*' --exclude='.storage' --exclude='*.log'
# Удаляем старые
find "${BACKUP_DIR}" -name 'ha-config-*.tar.gz' -mtime +${RETENTION_DAYS} -delete
# Пушим на удалённый (пример с rclone к S3)
rclone copy "${BACKUP_DIR}" "s3:my-bucket/ha-backups" --max-age 24h
Сделайте исполняемым: chmod +x /etc/cron.daily/ha-backup.
Предсказуемость — вот что даёт этот подход. Вы всегда знаете, где конфиг, как его вернуть, и почему он такой.
Часто задаваемые вопросы
Почему стоит выбрать Home Assistant Container, а не HA OS или Supervised?
Home Assistant Container удобен, если сервер уже работает под Linux и вы хотите управлять обновлениями, ресурсами и соседними сервисами через Docker Compose. HA OS и Supervised удобнее, когда нужны аддоны и управляемый жизненный цикл, но они требуют поддерживаемой платформы и не дают того же контроля над Docker-контейнерами. В Container аддоны не работают, поэтому Mosquitto, Zigbee2MQTT, ESPHome и базы данных запускаются отдельными контейнерами.
Зачем Home Assistant нужна сеть host и когда её можно заменить на обычную Docker-сеть?
Для обнаружения локальных устройств Home Assistant обычно нужен доступ к широковещательным и multicast-пакетам, поэтому режим network_mode: host упрощает работу mDNS, SSDP и UPnP. В режиме host контейнер использует сетевые интерфейсы узла напрямую, а не получает отдельный адрес в Docker-сети. Плата за это — отсутствие сетевой изоляции и риск конфликтов: Home Assistant слушает порты непосредственно на хосте, поэтому перед запуском проверьте, что они свободны. Если устройства находятся в другом VLAN, одного режима host недостаточно: понадобятся правила брандмауэра, маршрутизация multicast и IGMP.
Как подключить Zigbee-координатор, чтобы он не исчез после перезагрузки?
Сначала определите путь к координатору на хосте, например командой ls -l /dev/serial/by-id/, и передайте контейнеру Zigbee2MQTT именно этот стабильный путь через секцию devices в compose.yaml. Не задавайте /dev/ttyUSB0 как единственный идентификатор: после перезагрузки или отключения USB-устройства номера могут измениться. Затем укажите путь внутри контейнера в конфигурации Zigbee2MQTT и проверьте, что координатор появился в списке устройств. Если порт не появился, проверьте права доступа процесса контейнера к этому устройству.
Где хранятся настройки Home Assistant и как сделать резервную копию?
В режиме Container каталог /config должен быть смонтирован в именованный том Docker или в каталог на хосте, иначе настройки исчезнут после удаления контейнера. В compose.yaml заранее зафиксируйте путь и права доступа, а также регулярно сохраняйте копии /config: в нём находятся configuration.yaml, каталог .storage, секреты и база Recorder. Для согласованной резервной копии на уровне файлов остановите контейнер командой docker compose stop homeassistant, если сервис называется именно так, затем скопируйте каталог и запустите контейнер обратно. Конфигурации отдельных сервисов — Zigbee2MQTT, Mosquitto, Grafana и PostgreSQL — копируйте отдельно, а восстановление обязательно проверяйте на тестовом сервере.
Как обновлять Home Assistant предсказуемо и что делать при регрессии?
Для предсказуемого обновления сначала прочитайте примечания к релизу, зафиксируйте текущий тег образа и сделайте резервную копию. Тег stable удобен для первого запуска, но может смениться без изменения compose.yaml; в рабочем окружении лучше использовать конкретный релизный тег или digest. После проверки обновления выполните docker compose pull homeassistant и docker compose up -d homeassistant, затем проверьте docker compose logs --tail=200 homeassistant, состояние интеграций и доступность устройств. Если новая версия ломает сценарий, верните предыдущий тег и повторите эти команды, учитывая, что миграции конфигурации или базы данных могут сделать откат неполным.