On this page
I spent a while overthinking where to run Home Assistant. I have a four-node Proxmox cluster sitting there with cores to spare, and a NAS that is comparatively modest. Every instinct said “put the smart-home brain on the powerful machine.” That instinct is wrong, and it took me one honest look at how discovery works to see why. The machine’s specs barely matter. What matters is which building it is in.
This guide is the counterpart to running Home Assistant OS in a Proxmox VM: same question, opposite answer, because the deciding factor is not the hypervisor — it is the postcode. I will walk through how I placed it, the Docker Compose file I actually shipped, how I reach it from my phone without opening a single port, and how I wired it into monitoring the same afternoon.
The question everyone gets backwards
The usual way people choose where to run Home Assistant is by counting cores and RAM. That is the wrong criterion. Home Assistant’s job is to find and talk to your devices, and the protocols it uses to do that are deliberately local:
- mDNS (multicast DNS) is how a Chromecast, a printer, or an ESPHome sensor announces itself. It is a link-local protocol by design — the RFC that defines it uses a multicast address scoped to the local network, so routers do not forward it and it does not survive a hop across a VPN.
- SSDP (the discovery half of UPnP) works the same way: a local multicast shout that stays on the LAN.
- Local-push integrations expect the device and Home Assistant to be on the same subnet.
- USB radios for Zigbee and Z-Wave are physical. The stick has to be plugged into the machine running Home Assistant. You cannot plug a USB dongle into a server in another building.
A Tailscale tunnel will happily carry your dashboard between sites — you can sit at the office and toggle a light at home. What it will not carry is that local discovery multicast or a USB serial port. So the moment your cluster and your devices are in different places, the cluster is disqualified as the host no matter how fast it is.
My layout made the decision for me: the cluster is at the office; the NAS and every smart device are at the house. Here is the whole decision as a table.
| Where Home Assistant runs | Discovery + USB radios | Verdict |
|---|---|---|
| Home NAS (with the devices) | Works — same LAN, sticks plug in locally | ✅ Run it here |
| Offsite Proxmox cluster | Broken — multicast and USB don’t cross the tunnel | ❌ Powerful but blind |
| Wait for a future home cluster | Would work, but doesn’t exist yet | ⏳ Migrate later; HA’s own backup covers the move |
The NAS also had the room to spare — port 8123 was free and there was roughly 10 GB of RAM available — so this was not a case of squeezing onto a maxed-out box. It was the right box because of where it sits.
If a device needs to be discovered or has a radio, Home Assistant belongs on the same LAN as that device. Compute is the tiebreaker, never the deciding vote.
Container or the full appliance?
Once the NAS won, the next fork was how to install it. Home Assistant has several official install methods, and the two that matter here are Home Assistant Operating System (HAOS) and Home Assistant Container.
The official FAQ is blunt about the trade: Home Assistant Container is the plain Docker install of Home Assistant Core — no Supervisor, no add-on store. HAOS is the full appliance that does include the Supervisor and the add-on store, and it is happiest in its own dedicated VM — which is exactly what the HAOS-in-a-VM approach is for.
Losing the add-on store sounds like a downgrade until you notice what you get in return. On HAOS, things like your MQTT broker are add-ons managed by the Supervisor. On Container, each companion service is its own_ Docker container sitting in the same Compose stack the NAS already runs. That separation is a feature: restarting Home Assistant to load a new integration does not take your Zigbee network down with it, because Zigbee2MQTT is a different container that never restarted.
That is the trade in one sentence: you give up the one-click add-on store, and you get a smart-home hub whose blast radius on restart is exactly one container. For a NAS that already speaks Docker Compose, that is the better deal.
The Compose file I actually shipped
Here is the real shape of what runs, cleaned of my own addresses. The three lines that carry all the reasoning are image, network_mode, and restart.
Everything below is an example — swap in your own before you copy it:
/volume1/docker/homeassistant/config→ wherever your NAS keeps container config (Synology and Asustor use a/volume1root; yours may differ).TZ: America/Chicago→ your timezone.2026.8.1in the image tag → whatever the current stable release is when you deploy.- Any IP or hostname you see later (
10.0.0.10,homelab-nas) is a stand-in for your own.
Rule of thumb: if a value looks specific to one machine, it’s a placeholder to change — not a literal to copy.
Home Assistant’s own container docs use the official image at ghcr.io/home-assistant/home-assistant. I pin it to an exact version rather than :stable so an unattended pull never surprises me with a breaking release.
services:
homeassistant:
container_name: homeassistant
image: ghcr.io/home-assistant/home-assistant:2026.8.1
network_mode: host # required for mDNS/SSDP discovery
restart: unless-stopped # (I use "always" on the NAS — see below)
environment:
TZ: America/Chicago
volumes:
- /volume1/docker/homeassistant/config:/config
- /etc/localtime:/etc/localtime:ro
# Attaching a USB Zigbee/Z-Wave stick later? Add these then:
# privileged: true
# devices:
# - /dev/serial/by-id/usb-YOUR_COORDINATOR-if00:/dev/ttyUSB0
# Companion services stay commented until the hardware or credentials
# exist. Nothing dead ships live — but each is its own container, so an
# HA restart never drops the Zigbee mesh:
# mosquitto: # MQTT broker -> https://mosquitto.org
# zigbee2mqtt: # needs a USB Zigbee coordinator
# zwave-js-ui: # needs a Z-Wave stick -> https://github.com/zwave-js/zwave-js-ui
# matter-server: # Matter controller
image: …:2026.8.1— a pinned tag. Home Assistant ships a new version roughly every month, and those releases occasionally break things (2026.8, for example, started requiring UniFi Protect 7.1+). Pinning means you decide when to bump, after reading the release notes — the same discipline you would apply to any other production container.network_mode: host— the non-negotiable one. Home Assistant recommends host networking precisely so its discovery can reach the LAN. In a bridged network the multicast never arrives and integrations quietly find nothing. (The catch: host mode means you reach other containers bylocalhost:port, not by container name — the same Docker localhost trap that bites a lot of first stacks.)restart—unless-stoppedis the polite default that respects a manual stop. On the NAS I actually setrestart: always, because after a power blip I want the house’s brain back no matter what. Pick based on whether you ever want a manual stop to stick across a reboot.
docker compose up -d
docker compose logs -f homeassistant
Open http://10.0.0.10:8123 (your NAS’s LAN address) and finish onboarding. Do this now, not later.
A fresh install is an open onboarding page. Until you create the owner account, anyone who can reach that address on your LAN or tunnel can claim it and become the admin. Claim it the same day you deploy it — I did, and it is the cheapest security decision in this whole guide.
If you later lose that password, you do not have to start over: Home Assistant has an official offline reset (hass --script auth … change_password) that keeps your account and — importantly — your long-lived tokens. That is a full walkthrough in its own right, so it gets its own playbook.
Reaching it from anywhere without opening a port
I never expose Home Assistant to the internet. There is no port forward and no public hostname — the same posture I use for every other private service. Remote access rides on Tailscale instead, and this is the part that surprises people: the dashboard crosses the tunnel just fine, even though discovery never could.
| How I reach it | Address |
|---|---|
| On the home LAN | http://10.0.0.10:8123 |
| Over the tailnet (laptop, phone) | http://homelab-nas:8123 |
On my phone, the official Home Assistant iOS companion app points at the tailnet address and doubles as presence detection — it tells the house when I have come home. There is no official Home Assistant desktop app for Linux, so on the laptop I just open the web UI (in my case, through a dedicated tab in my homelab app, but any browser works).
If your smart devices worry you — and a cheap smart plug probably should — this pairs naturally with putting them on their own VLAN so your smart TV can’t see your NAS. Home Assistant bridges the segments deliberately; the devices don’t roam your whole network.
Wire it into monitoring on day one
The mistake I have made before is standing up a service and only noticing it died a week later. So the same afternoon I deployed Home Assistant, I gave it three monitoring hooks before I added a single automation.
The lightest possible net: an Uptime Kuma monitor hitting http://10.0.0.10:8123 on a 60-second interval. If the container falls over, I know in a minute.
An entry on my Homepage dashboard so Home Assistant lives next to everything else I run, one click away.
Home Assistant has a first-party Prometheus integration. You enable it with a single line in configuration.yaml, and it exposes everything at /api/prometheus behind a bearer token.
prometheus:
Then point Prometheus at /api/prometheus with a long-lived access token (the endpoint returns 401 without one). After a restart I had the scrape live and just over a hundred metric series flowing.
Getting that scrape to actually connect — from a monitoring box on a different site, across a subnet router, past a Tailscale ACL and the NAS firewall — turned into a three-layer debugging session that deserves its own post. This guide is the happy path; the diagnosis is the sequel.
What’s next: voice, without selling your privacy
The last thing on my list is voice, and it is the cleanest illustration of the whole “stay local” theme. The free, manual Alexa and Google integrations both require a public HTTPS endpoint for Home Assistant — exactly the internet exposure I have been avoiding. So those are out.
The two paths that respect the posture:
- A local voice pipeline. Home Assistant’s Assist already does text commands today. For spoken control you add speech-to-text and text-to-speech as their own Wyoming-protocol containers (Whisper and Piper) — which, on a Container install, is just two more services in the Compose stack. No cloud, no exposure.
- Home Assistant Cloud. The paid subscription from Nabu Casa (which also funds the project) handles the remote link and cloud voice for you without opening your instance to the world. Check their site for the current price.
Both are honest options. I am leaning local, because “two more containers on a stack I already run” is a very small ask for keeping my house off someone else’s server.
Related posts:
- Run Home Assistant on Proxmox the Right Way (HAOS in a VM) — the other answer to “where does HA go,” for when your hypervisor and your devices share a site
- Tailscale Subnet Router: Remote Homelab Access Without Port Forwarding — how the dashboard reaches you without any WAN exposure
- How to Actually Use a NAS with Your Homelab — why the NAS is the natural home for always-on services
- Your Smart TV Doesn’t Need to See Your NAS: VLANs for Beginners — walling off the smart devices Home Assistant controls
- Uptime Kuma: Dead-Simple Homelab Monitoring — the 60-second uptime check from day one
- One Dashboard for Your Whole Homelab: Set Up Homepage — where the Home Assistant tile lives
- What Is Prometheus? Homelab Metrics and Alerts Explained — the metrics backend behind the
/api/prometheusscrape - Your First Docker Compose Stack — the Compose foundation this build sits on
Recommended hardware for this setup:
- Zigbee USB coordinator — the stick that has to plug into the machine running Home Assistant, which is the whole reason it lives on the NAS
- Z-Wave USB controller — same story for Z-Wave devices
- USB extension cable — get the radio away from the NAS chassis; USB 3.0 and metal enclosures are notorious for jamming 2.4 GHz Zigbee
This post contains Amazon affiliate links (tag: buildahomelab-20). I earn a small commission on qualifying purchases at no extra cost to you.
Comments
Comments are powered by GitHub Discussions — sign in with a GitHub account to join the conversation.