Context
The stack currently has Pi-hole, Traefik, Homepage, Jellyfin, Portainer, Syncthing, FileBrowser, and (after issue #17) Netdata. This issue adds Home Assistant for home automation.
Key architectural constraint: Home Assistant must use network_mode: host so it can reach LAN devices and handle mDNS discovery. Like Pi-hole and Netdata, it cannot use Docker label-based Traefik routing — routing is via a traefik/dynamic/homeassistant.yml file pointing at host.docker.internal.
Critical prereq: homeassistant/config/configuration.yaml must exist on disk before the container first starts. If it doesn't, HA generates its own config without the trusted_proxies block, and every request through Traefik returns a 400 error. Create the file as part of this implementation.
Files to create/modify
- Create
homeassistant/config/configuration.yaml
- Create
traefik/dynamic/homeassistant.yml
- Modify
docker-compose.yml — add homeassistant service + wire HOMEPAGE_VAR_HOMEASSISTANT_API_KEY in homepage environment
- Modify
.env.example — add HOMEASSISTANT_API_KEY
- Modify
homepage/config/services.yaml — add Home Assistant entry under a new "Home" group
Implementation details
homeassistant/config/configuration.yaml
http:
use_x_forwarded_for: true
trusted_proxies:
- 172.16.0.0/12
- 127.0.0.1
docker-compose.yml — homeassistant service
homeassistant:
container_name: homeassistant
image: ghcr.io/home-assistant/home-assistant:stable
network_mode: host
privileged: true
environment:
TZ: ${TZ:-America/New_York}
volumes:
- ./homeassistant/config:/config
- /etc/localtime:/etc/localtime:ro
restart: unless-stopped
No Traefik labels (host network incompatible).
docker-compose.yml — homepage service environment
Add:
HOMEPAGE_VAR_HOMEASSISTANT_API_KEY: ${HOMEASSISTANT_API_KEY:-}
traefik/dynamic/homeassistant.yml
http:
routers:
homeassistant:
rule: "Host(`homeassistant.woggles.work`)"
entryPoints:
- websecure
tls:
certResolver: cloudflare
service: homeassistant-svc
services:
homeassistant-svc:
loadBalancer:
servers:
- url: "http://host.docker.internal:8123"
.env.example
# Home Assistant
# Get from HA UI: Profile > Security > Long-Lived Access Tokens (after first run)
HOMEASSISTANT_API_KEY=
homepage/config/services.yaml — new "Home" group
Add as the first group (above Media):
- Home:
- Home Assistant:
href: https://homeassistant.woggles.work
description: Home automation
icon: home-assistant
widget:
type: homeassistant
url: http://192.168.0.243:8123
key: "{{HOMEPAGE_VAR_HOMEASSISTANT_API_KEY}}"
fields: ["people_home", "lights_on", "switches_on"]
Note: widget URL uses LAN IP 192.168.0.243:8123, not a container name (HA is on host network).
Testing strategy
- Run
./scripts/lint-config.sh — must exit 0 before opening a PR.
- Verify
homeassistant/config/configuration.yaml exists and contains the trusted_proxies block — this is the most critical check.
- Check
docker-compose.yml:
network_mode: host on homeassistant (no networks: key)
- No Traefik labels on the homeassistant container
HOMEPAGE_VAR_HOMEASSISTANT_API_KEY: ${HOMEASSISTANT_API_KEY:-} in homepage service environment
- Check
traefik/dynamic/homeassistant.yml — router + service pointing to host.docker.internal:8123.
- Check
.env.example — HOMEASSISTANT_API_KEY= present with comment.
- Check
homepage/config/services.yaml — widget URL is http://192.168.0.243:8123 (LAN IP, not container name).
- Security review:
privileged: true is required by Home Assistant on Docker for device access — this is expected. API key is injected via env var, not hardcoded.
- README / scripts check: No new lint script changes needed. Consider whether a note about getting the HA API key post-first-run should be added to README or
.env.example comments (the .env.example comment above covers it).
- Full code review before opening PR — confirm no unrelated files changed.
Before opening the PR
- Run
./scripts/lint-config.sh — must exit 0.
- Open the PR against
main from a feature branch.
Context
The stack currently has Pi-hole, Traefik, Homepage, Jellyfin, Portainer, Syncthing, FileBrowser, and (after issue #17) Netdata. This issue adds Home Assistant for home automation.
Key architectural constraint: Home Assistant must use
network_mode: hostso it can reach LAN devices and handle mDNS discovery. Like Pi-hole and Netdata, it cannot use Docker label-based Traefik routing — routing is via atraefik/dynamic/homeassistant.ymlfile pointing athost.docker.internal.Critical prereq:
homeassistant/config/configuration.yamlmust exist on disk before the container first starts. If it doesn't, HA generates its own config without thetrusted_proxiesblock, and every request through Traefik returns a 400 error. Create the file as part of this implementation.Files to create/modify
homeassistant/config/configuration.yamltraefik/dynamic/homeassistant.ymldocker-compose.yml— addhomeassistantservice + wireHOMEPAGE_VAR_HOMEASSISTANT_API_KEYin homepage environment.env.example— addHOMEASSISTANT_API_KEYhomepage/config/services.yaml— add Home Assistant entry under a new "Home" groupImplementation details
homeassistant/config/configuration.yamldocker-compose.yml— homeassistant serviceNo Traefik labels (host network incompatible).
docker-compose.yml— homepage service environmentAdd:
traefik/dynamic/homeassistant.yml.env.examplehomepage/config/services.yaml— new "Home" groupAdd as the first group (above Media):
Note: widget URL uses LAN IP
192.168.0.243:8123, not a container name (HA is on host network).Testing strategy
./scripts/lint-config.sh— must exit 0 before opening a PR.homeassistant/config/configuration.yamlexists and contains thetrusted_proxiesblock — this is the most critical check.docker-compose.yml:network_mode: hoston homeassistant (nonetworks:key)HOMEPAGE_VAR_HOMEASSISTANT_API_KEY: ${HOMEASSISTANT_API_KEY:-}in homepage service environmenttraefik/dynamic/homeassistant.yml— router + service pointing tohost.docker.internal:8123..env.example—HOMEASSISTANT_API_KEY=present with comment.homepage/config/services.yaml— widget URL ishttp://192.168.0.243:8123(LAN IP, not container name).privileged: trueis required by Home Assistant on Docker for device access — this is expected. API key is injected via env var, not hardcoded..env.examplecomments (the.env.examplecomment above covers it).Before opening the PR
./scripts/lint-config.sh— must exit 0.mainfrom a feature branch.