Skip to content

Add Home Assistant service #18

@riccjohn

Description

@riccjohn

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

  1. Run ./scripts/lint-config.sh — must exit 0 before opening a PR.
  2. Verify homeassistant/config/configuration.yaml exists and contains the trusted_proxies block — this is the most critical check.
  3. 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
  4. Check traefik/dynamic/homeassistant.yml — router + service pointing to host.docker.internal:8123.
  5. Check .env.exampleHOMEASSISTANT_API_KEY= present with comment.
  6. Check homepage/config/services.yaml — widget URL is http://192.168.0.243:8123 (LAN IP, not container name).
  7. 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.
  8. 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).
  9. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions