Every SmartHome enthusiast loves Home Assistant. And so do I! But, with Home Assistant as your central brain and heart of your SmartHome, downtimes are nothing you want to have or run into. So I thought a lot about what would be the best, yet affordable, hardware setup to make Home Assistant as reliable as possible. Also only with a reasonable amount of work and maintenance (sorry, no large Kubernetes multi-node cluster with dedicated CEPH storage 🙂 ).

I landed for my use case on a four node RaspberryPi4 8GB cluster, which I setup with portainer and traefik as a load balancer, you can read more about it in my recent guide here. It also uses a Synology shared persistent NFS storage which can be accessed by all nodes. So if one node fails and the container gets automatically relaunched on a new node, it will take over all configurations and settings from the previous Home Assistant instance.

The easiest way to get started is to create your storage folders and then deploy Home Assistant together with influxdb, mariadb and grafana via a docker-compose.yml. So lets get started!

First you want to create the following folders in your nfs shared storage of your docker swarm cluster. ssh into your node and create the folders with (change path according to your structure):

mkdir /mnt/docker/homeassistant
mkdir /mnt/docker/homeassistant/config
mkdir /mnt/docker/homeassistant/grafana
mkdir /mnt/docker/homeassistant/mariadb
mkdir /mnt/docker/homeassistant/mariadb/db
mkdir /mnt/docker/homeassistant/influxdb
mkdir /mnt/docker/homeassistant/textconfigurator
touch /mnt/docker/homeassistant/docker-compose.yml
touch /mnt/docker/homeassistant/mariadb/my.cnf
touch /mnt/docker/homeassistant/grafana/custom.ini

Now we have created all folders and files we want to mount in our docker-compose file. To be sure we have sufficient rights, own the folder recursively with your used username. For example:

sudo chown username:username -R /mnt/docker/homeassistant/

This should ensure your are not running in any future access right problem on the other nodes.

Now we need to set some docker secret variables that we are using later in the compose file. Run these commands and replace the values with your own, very long and save passwords (Note: don't forget the dash "-" at the end of the command!):

echo "SUPERLONGCOMPLEXPASSWORDSTRING" | docker secret create ha_db_password -
echo "SUPERLONGCOMPLEXPASSWORDSTRING" | docker secret create mysql_root_password -
echo "SUPERLONGCOMPLEXPASSWORDSTRING" | docker secret create influxdb_user_password -
echo "SUPERLONGCOMPLEXPASSWORDSTRING" | docker secret create hass_configurator_password -

Now we have stored your own database passwords secure and save into our docker vault. Important to note down the passwords you used, as we need them later. Next we also need to store our Home Assistant API Key in here. If you don't have one yet, you can also run that command later:

echo "SUPERLONGCOMPLEXAPIKEY" | docker secret create hass_api_password -

Now we are ready to run and deploy Home Assistant on our nodes via docker-compose. Edit the docker compose file with:

nano /mnt/docker/homeassistant/docker-compose.yml

And copy & paste the following code into it:

version: '3.8'
services:
#homeassistant
  homeassistant:
    image: homeassistant/raspberrypi4-64-homeassistant:latest
    ports:
      - "8443:8443"
      - "51827:51827"
      - "5353:5353"
    volumes:
      - /mnt/docker/homeassistant/config:/config
      - /etc/localtime:/etc/localtime:ro
    networks:
      - proxy
    environment:
      - TZ=Europe/Berlin
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints:
          - node.role == manager
      labels:
        - "traefik.enable=true"
        - "traefik.docker.network=proxy"
        - "traefik.port=8443"
        - "traefik.backend=homeassistant"
        - "traefik.http.routers.homeassistant.entrypoints=http"
        - "traefik.http.routers.homeassistant.rule=Host(`homeassistant.yourdomain.com`)"
        - "traefik.http.middlewares.homeassistant-https-redirect.redirectscheme.scheme=https"
        - "traefik.http.routers.homeassistant.middlewares=homeassistant-https-redirect"
        - "traefik.http.routers.homeassistant-secure.entrypoints=https"
        - "traefik.http.routers.homeassistant-secure.rule=Host(`homeassistant.yourdomain.com`)"
        - "traefik.http.routers.homeassistant-secure.tls=true"
        - "traefik.http.routers.homeassistant-secure.service=homeassistant"
        - "traefik.http.services.homeassistant.loadbalancer.server.port=8080"

#config editor
  hass-configurator:
    image: "causticlab/hass-configurator-docker:latest"
    ports:
      - "3218:3218/tcp"
    networks:
      - proxy
    volumes:
      - "/mnt/docker/homeassistant/textconfigurator:/config"
      - "/mnt/docker/homeassistant/config:/hass-config"
    environment:
      - HC_USERNAME=YOURWISHUSERNAME
      - HC_PASSWORD=/run/secrets/hass_configurator_password
      - HC_HASS_API_PASSWORD=/run/secrets/hass_api_password
    deploy:
      labels:
        - "traefik.enable=true"
        - "traefik.docker.network=proxy"
        - "traefik.port=3218"
        - "traefik.backend=homeassistant-configurator"
        - "traefik.http.routers.homeassistant-configurator.entrypoints=http"
        - "traefik.http.routers.homeassistant-configurator.rule=Host(`homeassistant-configurator.local.yourdomain.com`)"
        - "traefik.http.middlewares.homeassistant-configurator-https-redirect.redirectscheme.scheme=https"
        - "traefik.http.routers.homeassistant-configurator.middlewares=homeassistant-https-redirect"
        - "traefik.http.routers.homeassistant-configurator-secure.entrypoints=https"
        - "traefik.http.routers.homeassistant-configurator-secure.rule=Host(`homeassistant-configurator.local.yourdomain.com`)"
        - "traefik.http.routers.homeassistant-configurator-secure.tls=true"
        - "traefik.http.routers.homeassistant-configurator-secure.service=homeassistant-configurator"
        - "traefik.http.services.homeassistant-configurator.loadbalancer.server.port=3218"

#influxDB
  influxdb:
    image: influxdb:latest
    networks:
      - proxy
    ports:
      - "8086:8086"
      - "8083:8083"
    environment:
      INFLUXDB_DB: homeassistant
      INFLUXDB_USER: homeassistant
      INFLUXDB_USER_PASSWORD: /run/secrets/influxdb_user_password
    volumes:
      - /mnt/docker/homeassistant/influxdb/:/var/lib/influxdb
      - /etc/localtime:/etc/localtime:ro

#grafana
  grafana:
    depends_on:
      - influxdb
    image: grafana/grafana:latest
    networks:
      - proxy
    ports:
      - "3000:3000"
    environment:
      - GF_INSTALL_PLUGINS=grafana-clock-panel,briangann-gauge-panel,natel-plotly-panel,grafana-simple-json-datasource
    volumes:
      - /mnt/docker/homeassistant/grafana/custom.ini:/etc/grafana/grafana.ini
      - /mnt/docker/homeassistant/grafana/:/var/lib/grafana
    deploy:
      labels:
        - "traefik.enable=true"
        - "traefik.docker.network=proxy"
        - "traefik.port=3000"
        - "traefik.backend=homeassistant-grafana"
        - "traefik.http.routers.homeassistant-grafana.entrypoints=http"
        - "traefik.http.routers.homeassistant-grafana.rule=Host(`homeassistant-grafana.local.yourdomain.com`)"
        - "traefik.http.middlewares.homeassistant-grafana-https-redirect.redirectscheme.scheme=https"
        - "traefik.http.routers.homeassistant-grafana.middlewares=homeassistant-https-redirect"
        - "traefik.http.routers.homeassistant-grafana-secure.entrypoints=https"
        - "traefik.http.routers.homeassistant-grafana-secure.rule=Host(`homeassistant-grafana.local.yourdomain.com`)"
        - "traefik.http.routers.homeassistant-grafana-secure.tls=true"
        - "traefik.http.routers.homeassistant-grafana-secure.service=homeassistant-grafana"
        - "traefik.http.services.homeassistant-grafana.loadbalancer.server.port=3000"

#mariadb
  mariadb:
    image: mariadb:latest
    ports:
      - 3306:3306
    secrets:
      - ha_db_password
      - mysql_root_password
    environment:
      - MYSQL_USER=ENTERYOURUSER
      - MYSQL_DATABASE=home_assistant
      - MYSQL_PASSWORD_FILE=/run/secrets/ha_db_password
      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_password
    volumes:
      - /mnt/docker/mariadb/db:/var/lib/mysql
      - /mnt/docker/mariadb/my.cnf:/etc/mysql/my.cnf:ro
      - /etc/localtime:/etc/localtime:ro
    networks:
      - proxy
    deploy:
      placement:
        constraints: [node.role == manager]
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

#mariadb admin interface
  adminer:
    image: adminer
    ports:
      - 8280:8080
    networks:
      - proxy
    deploy:
      replicas: 1
      labels:
        - "traefik.enable=true"
        - "traefik.docker.network=proxy"
        - "traefik.port=8280"
        - "traefik.http.routers.adminer.entrypoints=http"
        - "traefik.http.routers.adminer.rule=Host(`mariadb.local.yourdomain.com`)"
        - "traefik.http.middlewares.adminer-https-redirect.redirectscheme.scheme=https"
        - "traefik.http.routers.adminer.middlewares=adminer-https-redirect"
        - "traefik.http.routers.adminer-secure.entrypoints=https"
        - "traefik.http.routers.adminer-secure.rule=Host(`mariadb.local.yourdomain.com`)"
        - "traefik.http.routers.adminer-secure.tls=true"
        - "traefik.http.routers.adminer-secure.service=adminer"
        - "traefik.http.services.adminer.loadbalancer.server.port=8080"

secrets:
  ha_db_password:
    external: true
  mysql_root_password:
    external: true
  hass_configurator_password:
    external: true
  hass_api_password:
    external: true
  influxdb_user_password:
    external: true

networks:
  proxy:
    external: true

PLEASE make sure to read through it and replace all placeholders with your own values. Specifically the usernames of the services and the custom traefik domain labels "local.yourdomain.com" with your own domain settings.

If you are NOT using traefik at all, you can also delete the full "labels:..." section of each service.

Now if you have read through the whole file and replaced every necessary value, save and exit the file with CTRL + X, enter and y to confirm.

Now you can deploy the file using this command:

docker stack deploy -c /mnt/docker/homeassistant/docker-compose.yml homeassistant

If you are using portainer you should see the new "homeassistant" service being created and running with all its sub instances of grafana, mariadb and influxdb.

When every container started running you should be able to access the Home Assistant web interface via:

https://192.168.x.x-IP-of-Cluster-Node:8443/

On your first start Home Assistant will guide you through user creation and some first basic settings. Once you are done and are on the main dashboard, go back to your ssh console and edit the configuration.yml.

We need to add some first basic settings to tell Home Assistant that we are using grafana, influxdb and mariadb. We also add direct access to your text-editor container via iframe, so we can edit the configuration files right from within Home Assistant. The configuration.yml should be created by now when you first went through the setup guide.

nano /mnt/docker/homeassistant/config/configuration.yml

Replace its contents with:


# Configure a default setup of Home Assistant (frontend, api, etc)
default_config:

# include external config files
group: !include groups.yaml
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml

panel_iframe:
  file_editor:
    title: 'File Editor'
    url: 'https://homeassistant-configurator.local.yourdomain.com'
    icon: mdi:wrench-outline
  grafana:
    title: 'Grafana'
    url: 'https://homeassistant-grafana.local.yourdomain.com'
    icon: mdi:chart-timeline

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.30.33.3
    - 172.0.0.1
    - ::1
    - 10.0.0.2
  server_port: 8443
      
# MariaDB integration
recorder:
  db_url: !secret mariadb
  purge_keep_days: 3
  auto_purge: true
  commit_interval: 60
  include:
    domains:
      - sensor
      - binary_sensor
      - switch
      - automation
      - light
      - media_player
      - updater

# influxDB integration
influxdb:
  host: localhost
  port: 8086
  username: !secret influxdb_user
  password: !secret influxdb_password
  database: !secret influxdb_database
  max_retries: 5
  default_measurement: state
  include:
    domains:
      - switch
      - light
      - sensor
      - binary_sensor
      - device_tracker
  exclude:
    entities:
      - sensor.icloud3_event_log

Also read this config carefully and replace the necessary usernames and domains with your values before you save and exit the file. The !secret values can either be set in the secrets files or if you are lazy and won't be too secure, you can just copy and paste the passwords you set earlier in here directly (not recommended).

To set the passwords as !secrets in Home Assistant, edit this file:

nano /mnt/docker/homeassistant/config/secrets.yml

Add these secret values to the file:

# Use this file to store secrets like usernames and passwords.
# Learn more at https://www.home-assistant.io/docs/configuration/secrets/
some_password: welcome
mariadb: 'mysql://youruser:[email protected]:3306/home_assistant?charset=utf8'
influxdb_user: homeassistant
influxdb_password: YOURINFLUXDBPASS
influxdb_database: homeassistant

Replace the placeholders "yourpassword", "youruser" and IP with your values. Save and exit the file as usual with CTRL + X and confirm with Y and enter.

Now we check our configuration and restart Home Assistant to load our config changes. In the Home Assistant web interface go to "Configuration" -> "Settings" and click "Check configuration". If it returns green and says all good you can restart the server with the below red Restart button under "Server management". If you get a red error, check the configuration.yml again on typos or missed annotations.

After the restart, Home Assistant should come back online and is now using influxDB, mariaDB and has 2 extra links in the menu for "Text Editor" and "Grafana".

Congratulations, you finished a basic Home Assistant install on a Docker Swarm cluster with custom databases (influxDB and mariaDB).