You Picked the Wrong Install Method and Now You’re Paying for It
Here’s a scenario that plays out in home lab forums about three times a week: someone sets up Home Assistant Container because they already have Portainer, Traefik, and thirty other services humming along in Docker Compose. Clean, minimal, no magic. Then they try to add Frigate, or the Mosquitto broker, or ESPHome — and they hit the wall.
“Where’s the Add-On store?”
It’s not there. The Add-On store is a Supervisor feature, and Supervisor is not part of HA Container. So now you’re either rebuilding your entire setup on HAOS, or you’re wiring up Mosquitto and Zigbee2MQTT yourself like some kind of responsible adult who reads documentation.
Neither answer is obviously wrong. But you need to understand what you signed up for — ideally before you’re three hours into a Sunday afternoon that was supposed to be quick.
The Four Flavors — Actually Read This Part
Home Assistant ships as four distinct installation methods, and the community does a terrible job making this clear on first encounter.
HAOS (Home Assistant Operating System) — A full Linux OS image. Boots straight into HA. The Supervisor orchestrates everything: Add-Ons, snapshots, OS updates, the whole appliance. Runs on a Pi, an x86 mini-PC, or a VM. This is what the official docs point you at, and for good reason.
HA Supervised — The Supervisor daemon running on your own Debian install. You get the Add-On store and the backup magic, but you’re responsible for the underlying OS. The officially supported OS is Debian (specific version, no deviations). Deviate, and the UI will passive-aggressively flag you as “unsupported” forever.
HA Container — Just the Home Assistant Core process in a Docker container. No Supervisor. No Add-On store. No built-in backup orchestration. You bring your own infrastructure.
HA Core — A Python virtualenv install. Nobody should be doing this manually in 2026 unless they have a very specific reason.
The practical choice is almost always HAOS vs Container. Supervised exists in a weird middle ground that requires Debian discipline most home labbers won’t maintain, and Core is for edge cases.
What Add-Ons Actually Are
Add-Ons are Docker containers. That’s it. The Supervisor pulls an OCI image, starts it, and wires it into a shared networking namespace with Home Assistant. The Add-On store is essentially a curated container registry with a point-and-click interface and opinionated defaults.
When you install the Mosquitto broker Add-On, you’re getting a container running eclipse-mosquitto with a config file the Supervisor manages, credentials automatically shared with HA, and a log viewer baked into the UI. When you click “Update,” Supervisor pulls the new image, stops the old one, and starts the new one. When you take a snapshot, it includes Mosquitto’s config and data.
That’s genuinely nice. The friction is near zero for a standard setup.
The trade-off is control. You’re not editing mosquitto.conf directly — you’re going through the Add-On’s configuration panel, which exposes the options the maintainer decided to surface. If you want per_listener_settings true and it’s not in the panel, you’re either finding a workaround or living without it.
The Compose Stack You’d Build Anyway
If you’re running HA Container, you’re already comfortable with Compose files. Here’s the full stack for the services people usually reach for Add-Ons to cover.
Home Assistant Core
services: homeassistant: image: ghcr.io/home-assistant/home-assistant:stable container_name: homeassistant restart: unless-stopped privileged: true network_mode: host volumes: - ./config:/config - /etc/localtime:/etc/localtime:ro environment: - TZ=America/Chicagonetwork_mode: host is not laziness — HA uses mDNS, SSDP, and broadcast discovery for device detection. Bridge networking breaks a surprising number of integrations.
Mosquitto MQTT Broker
mosquitto: image: eclipse-mosquitto:2 container_name: mosquitto restart: unless-stopped ports: - "1883:1883" - "9001:9001" volumes: - ./mosquitto/config:/mosquitto/config - ./mosquitto/data:/mosquitto/data - ./mosquitto/log:/mosquitto/loglistener 1883listener 9001protocol websocketsallow_anonymous falsepassword_file /mosquitto/config/passwdpersistence truepersistence_location /mosquitto/data/log_dest file /mosquitto/log/mosquitto.logCreate your password file before starting:
docker run --rm -it \ -v $(pwd)/mosquitto/config:/mosquitto/config \ eclipse-mosquitto:2 \ mosquitto_passwd -c /mosquitto/config/passwd youruserZigbee2MQTT
zigbee2mqtt: image: koenkk/zigbee2mqtt:latest container_name: zigbee2mqtt restart: unless-stopped volumes: - ./zigbee2mqtt/data:/app/data - /run/udev:/run/udev:ro ports: - "8080:8080" environment: - TZ=America/Chicago devices: - /dev/ttyUSB0:/dev/ttyUSB0 depends_on: - mosquittohomeassistant: truepermit_join: falsemqtt: base_topic: zigbee2mqtt server: mqtt://mosquitto:1883 user: youruser password: yourpasswordserial: port: /dev/ttyUSB0frontend: port: 8080advanced: log_level: infoESPHome
esphome: image: ghcr.io/esphome/esphome:stable container_name: esphome restart: unless-stopped volumes: - ./esphome/config:/config - /etc/localtime:/etc/localtime:ro ports: - "6052:6052" network_mode: hostESPHome also benefits from host networking for mDNS device discovery. If you’re flashing over USB, map the device through. If you’re doing OTA only, you can get away without it.
Frigate NVR
frigate: image: ghcr.io/blakeblackshear/frigate:stable container_name: frigate restart: unless-stopped privileged: true shm_size: "256mb" devices: - /dev/bus/usb:/dev/bus/usb # Coral USB - /dev/dri/renderD128:/dev/dri/renderD128 # iGPU for decoding volumes: - /etc/localtime:/etc/localtime:ro - ./frigate/config:/config - ./frigate/media:/media/frigate - type: tmpfs target: /tmp/cache tmpfs: size: 1000000000 ports: - "5000:5000" - "8554:8554" # RTSP - "8555:8555/tcp" # WebRTC - "8555:8555/udp" environment: - FRIGATE_RTSP_PASSWORD=yourpassword depends_on: - mosquittoNode-RED
node-red: image: nodered/node-red:latest container_name: node-red restart: unless-stopped ports: - "1880:1880" volumes: - ./node-red/data:/data environment: - TZ=America/Chicago depends_on: - homeassistant - mosquittoAll of this in one docker-compose.yml. Single docker compose up -d and you’re running the same stack that would take you fifteen minutes of clicking through Add-On store pages on HAOS. The difference: you built it, you understand it, and mosquitto.conf says exactly what you told it to say.
Where HAOS Wins Without Argument
Backups
The Supervisor backup is genuinely impressive. One click, full snapshot: HA config, Add-On data, custom components, everything. Restores to bare metal. Takes two minutes to move your entire HA instance to new hardware.
With Container, you’re rolling your own backup strategy. Something like:
#!/bin/bashBACKUP_DIR=/mnt/nas/homeassistant-backupsDATE=$(date +%Y%m%d-%H%M)
# Stop services gracefullydocker compose stop
# Archive the whole stacktar -czf "$BACKUP_DIR/ha-stack-$DATE.tar.gz" \ ./config \ ./mosquitto \ ./zigbee2mqtt \ ./esphome \ ./frigate/config \ ./node-red/data
docker compose start
echo "Backup complete: ha-stack-$DATE.tar.gz"This works. It’s just not magic. You’re also responsible for rotating old backups, testing restores, and remembering that Frigate’s media directory is probably not worth backing up at full fidelity.
OS Updates and Watchdog
HAOS handles host OS updates, kernel patches, and restarts through the Supervisor. The system is self-contained. For a Pi sitting in your wiring closet that you want to forget about, this is the correct approach.
Container on a shared Debian host means you’re also managing that host’s apt updates, kernel reboots, and whatever else is running on the same box. More flexibility, more surface area.
Add-On Ecosystem is Fast
The official Add-On store and community repos (via HACS Addons, for instance) ship updates quickly and the UX is genuinely zero-effort. For Zigbee2MQTT specifically, the Add-On ships configuration.yaml changes in a UI panel. For a non-technical household member trying to add a device, this matters.
Where Container Wins
You’re Already Running 30 Services
If Traefik, Vaultwarden, Immich, Jellyfin, and Gitea are all in your Compose stack, adding HA as one more service is trivial. You already have Traefik routing HTTPS, you already have a backup strategy, you already understand container lifecycle.
Adding HAOS would mean a separate machine (or VM) with its own networking, its own update cycle, its own backup destination. The integration overhead often outweighs the Add-On convenience.
Resource Efficiency
HAOS carries the full OS layer, the Supervisor daemon, and the Docker runtime inside the appliance. On a Pi 4 with 4GB RAM, it’s fine. On a 2GB Pi or a resource-constrained VM, the Supervisor overhead matters.
HA Container is lean. The home-assistant container on its own uses around 300-400MB RAM at idle. No Supervisor, no watchdog, no OS layer you don’t control.
Upstream Container Control
When Mosquitto ships a security patch, you decide when you update. docker compose pull && docker compose up -d. The Add-On store ties you to the Add-On maintainer’s release cadence — usually fast, occasionally not.
If you’re running a modified Frigate config with custom model weights or a pinned version for stability, Container gives you exactly that.
HACS Is Orthogonal to All of This
Quick clarification because this trips people up: HACS (Home Assistant Community Store) handles custom integrations and frontend cards — Mushroom Cards, browser_mod, custom components. It installs into the HA config directory and works on any install method, including Container.
HACS is not the Add-On store. You can run HACS on HA Container and still manually manage Mosquitto in Compose. These are completely separate systems. Install HACS regardless of your install method if you want the community integration ecosystem.
Migration Paths
HAOS → Container
If you’re moving from HAOS to a Compose setup, the HA config is portable. The database (home-assistant_v2.db) and your configuration.yaml, automations.yaml, scripts.yaml all move cleanly.
- Take a full HAOS snapshot from the UI
- Extract the
.tar— inside is ahomeassistant.tar.gzwith your config directory - Drop that content into
./config/in your Compose setup - Migrate Add-On data manually: Mosquitto passwords, Zigbee2MQTT
database.db, Node-RED flows - Start HA Container, verify your history loaded, re-pair any integrations that need device discovery
The recorder database transfers intact. Your 90-day history comes with you.
Container → HAOS
Reverse it. Copy your configuration.yaml and related files into a fresh HAOS install, restore the recorder DB, reinstall integrations. The Add-On configs start fresh — you’ll need to re-enter Mosquitto credentials and Zigbee2MQTT config through the UI panels. Annoying but not catastrophic.
Making the Call
Pick HAOS if:
- The machine is dedicated to Home Assistant
- You want “it just works” for software-defined home automation
- Non-technical household members might touch the UI
- You value the one-click backup/restore over everything else
- You’re on a Pi or mini-PC and don’t have 30 other services competing for resources
Pick Container if:
- Home Assistant is one service among many on a shared host
- You already have a Compose-based infrastructure with Traefik, monitoring, backups
- You want precise control over every service’s config and update cadence
- You’re comfortable in a terminal and read documentation for fun (you’re here, so probably yes)
- You need custom Mosquitto ACLs, pinned Frigate versions, or modified ESPHome images
The Bottom Line
Add-Ons are Docker containers wearing a nicer suit and managed by someone else. That’s a good deal when you don’t want to be the someone else. It’s a frustrating limitation when you already are.
HAOS is the right default for a dedicated HA appliance. You get backups that actually work, a curated ecosystem of Add-Ons, and an OS that keeps itself updated. For a Pi in the closet running nothing but home automation, it’s the correct answer 90% of the time.
Container is the right choice when HA is one service in a larger homelab stack. The Add-On store is gone, but you get everything Compose offers: version pinning, custom configs, shared networking with your other services, and backup strategies that fit your existing workflow.
The only wrong answer is picking an install method without knowing what you gave up, then spending a Sunday afternoon wondering why the Add-On store tab doesn’t exist.
Your docker-compose.yml is waiting.