Home Assistant security checklist — hardening before you expose the smart home
Home Assistant is the single highest-value target in most homelabs. It speaks to your locks, your cameras, your alarm panel, every smart plug, and increasingly your car. A compromise isn't a stolen Plex library — it's somebody opening your garage door at 3am. This is the twelve-check hardening pass to run before HA goes anywhere near the internet, and to re-run every few months because HA changes fast and the attack surface drifts with it.
For the general pre-exposure pattern this post specialises, see Before you expose a service to the web. For the wider category of Grafana / Portainer / Pi-hole / HA admin UIs that get found by Shodan within hours, see exposed admin surfaces — the #1 homelab compromise vector. Both apply here verbatim; this post is the HA-specific layer on top.
Step 1 — Decide whether HA needs to be public at all
Before any hardening, ask the only question that obviates most of it: does Home Assistant actually need a public DNS record and an open port? The answer for the overwhelming majority of users is no. You want remote access from your phone — that is not the same thing as exposing port 8123 to the entire internet.
Three good remote-access patterns, in roughly increasing order of effort:
- Nabu Casa Home Assistant Cloud. Operated by the Home Assistant company. Around $6.50–$7.50/month at current rates (check nabucasa.com for your region), supports the iOS and Android apps over their relay, no public port, no DNS, no certificate to manage. The default answer for non-technical households.
- Cloudflare Tunnel + Cloudflare Access. Free for personal use. No inbound port on your network. Cloudflare originates the connection to your box and applies SSO at the edge (Google, GitHub, one-time PIN). Pair with HA's own auth and you have two independent gates.
- Tailscale / Headscale / WireGuard. The smart-home device itself stays LAN-only; your phone and laptop join a WireGuard mesh. Zero public attack surface. The HA Companion app supports this out of the box.
If any of these solve your problem — and one of them almost certainly does — choose one and stop. The rest of this checklist still matters (LAN-only HA can still be pivoted to from a compromised IoT device), but the threat model collapses from "anyone on the internet" to "anyone already on my network." That is the single biggest reduction available.
Step 2 — Audit auth providers and the trusted_networks footgun
Open configuration.yaml and look at the
homeassistant.auth_providers block. The default and
correct setup is a single
homeassistant provider — username and password, with
HA's own bcrypt hashing. Anything else needs scrutiny.
homeassistant:
auth_providers:
- type: homeassistant
# NOT this, ever, on an internet-facing HA:
# - type: trusted_networks
# trusted_networks:
# - 192.168.1.0/24
# - 172.16.0.0/12
The trusted_networks provider tells HA "for these
IP ranges, skip authentication entirely." On a LAN-only install
that's fine. The footgun: the moment you put HA behind a reverse
proxy, the source IP HA sees is the proxy, not the real client.
If you've added the proxy's IP to trusted_networks,
every request from the internet now bypasses auth.
Several public HA compromises trace exactly to this misconfig.
Settle on this rule: never trust an IP range that contains an
internet-facing reverse proxy. If you need a passwordless LAN
experience, use the allow_bypass_login flag on a
named user with strong defence-in-depth elsewhere, not blanket
IP trust.
While you're in the auth UI (Settings → People → Users), audit the user list. Every user account that exists has a reachable password. Remove the test accounts. Demote anyone who doesn't need admin to a non-admin user. Force MFA on every admin account — HA supports TOTP natively.
Step 3 — Rotate long-lived access tokens
Long-lived access tokens (LLATs) are HA's bearer credentials for
external integrations: Grafana scraping HA, an automation in
Node-RED, the
hass CLI from another host, a custom Lovelace card
that calls the REST API. They never expire. They have full user
permissions. If one leaks into a Git repo, a Discord paste, or a
shared screenshot, the leaker is the attacker.
Audit them in your profile → Long-lived access tokens. You're looking for:
- Tokens issued to integrations you no longer use.
- Tokens older than twelve months — rotate on principle.
- Tokens with generic names like "test" or "token1" — you no longer know what they do; delete and regenerate as needed.
Every LLAT belongs in a secrets store on the consumer side
(1Password, Bitwarden, a .env file outside Git),
never in a committed config file. See Step 7 for the secrets
story end-to-end.
Step 4 — Reverse proxy + TLS, never expose raw 8123
Home Assistant's built-in HTTP server is fine for LAN use, but it isn't where you want internet traffic terminating. Put it behind a reverse proxy that handles TLS, request inspection, and rate limiting. Caddy, Traefik, and Nginx Proxy Manager are the three common picks; all three do Let's Encrypt automatically.
A minimal Caddy config:
ha.example.com {
reverse_proxy 192.168.1.50:8123 {
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
encode gzip
header Strict-Transport-Security "max-age=31536000; includeSubDomains"
}
Then tell HA to honour the proxy headers — and only the proxy:
http:
use_x_forwarded_for: true
trusted_proxies:
- 192.168.1.10 # the proxy's LAN IP, nothing wider
ip_ban_enabled: true
login_attempts_threshold: 5
trusted_proxies must be the narrowest CIDR that
contains your reverse proxy — never 0.0.0.0/0,
never 10.0.0.0/8, never the IoT VLAN. If HA trusts
a wider range, attackers can spoof
X-Forwarded-For and bypass IP bans.
Confirm port 8123 is firewalled at the host. The only thing on your network that should be able to reach 8123 directly is the reverse proxy box. See TLS certificate expiry on self-hosted services for the renewal side; a HA reverse proxy that quietly fails to renew is the kind of outage you find out about from a confused household at 8am.
Step 5 — Supervisor add-ons are individual admin surfaces
Home Assistant OS and Supervised installs come with the add-on store: AdGuard Home, ESPHome, Zigbee2MQTT, Node-RED, File Editor, SSH & Web Terminal, Mosquitto, InfluxDB, Grafana, Frigate. Most of them ship a web UI of their own.
Two settings on every add-on warrant attention:
- Show in sidebar / Web UI. Toggles the embedded admin UI. Fine if reached via HA's Ingress (which enforces HA's auth). Not fine if the add-on also binds to a host network port, because that port skips HA entirely.
- Network configuration. Add-ons can request
host ports —
1880/tcpfor Node-RED,8123/tcpfor old HACS bridges,5000/tcpfor Frigate. Every host port the add-on opens is its own admin surface that has to be auth'd or firewalled independently.
Check from the HA host:
ss -tlnp | grep -v '127.0.0.1\|::1'
Anything bound to 0.0.0.0 or your LAN IP is an
exposed admin surface. For each one, decide: does it need to be
reachable outside HA Ingress at all? If not, set the add-on's
network config to leave the port blank — HA will only expose it
via the authenticated Ingress proxy.
The SSH & Web Terminal add-on deserves a special mention. The community version supports SSH key authentication and a hardened sshd config; the "official" version is friendlier but historically defaults less strictly. Either way, disable password auth and only allow specific keys. See SSH key hygiene for homelabs for the audit.
Step 6 — HACS and custom integrations are a supply chain
HACS — the Home Assistant Community Store — pulls integrations directly from arbitrary GitHub repositories. It is enormously useful and it is also the single largest source of supply-chain risk in a typical HA install. Custom integrations run in the HA process. They have full access to HA's secrets, every entity, every connected device.
Treat them accordingly:
- Pin versions. HACS lets you select specific tags. Don't autoupdate to the latest commit. Read the changelog before bumping.
- Audit the repo. Star count and "this works for me" comments are not a security review. Check who owns the repo, when it was last updated, whether the code is small enough that you can skim it.
- Remove what you don't use.
config/custom_components/accumulates. Each entry is loaded at HA start and runs with full privileges. Prune ruthlessly. - Watch for repo transfers. A popular HACS repo being silently transferred to a new owner who then pushes a malicious release is the realistic supply-chain scenario. GitHub will email you about repo transfers if you've starred the repo; turn those notifications on.
The same logic applies to custom Lovelace cards, themes, and AppDaemon apps. Any code you didn't write that runs inside HA can read every token, every API key, every camera feed. Audit it like you would a server-side dependency.
Step 7 — Move every secret out of YAML and Git
configuration.yaml, automation YAML, script YAML —
none of these should contain a single literal API key, password,
or token. Everything goes through secrets.yaml with
!secret references:
# secrets.yaml — NEVER committed
mqtt_password: "long-random-string"
notify_pushover_token: "azGD..."
spotify_client_secret: "1c2d..."
# configuration.yaml — safe to commit
mqtt:
broker: 127.0.0.1
password: !secret mqtt_password
notify:
- platform: pushover
api_key: !secret notify_pushover_token
Add secrets.yaml, known_devices.yaml,
and the entire .storage/ directory to
.gitignore the moment you initialise the repo.
Audit existing history with
trufflehog
before pushing to a public remote:
trufflehog filesystem /config
trufflehog git file:///path/to/your/ha-config.git
If anything leaked, assume the secret is burned. Rotate the
credential at the upstream service, then rewrite Git history
with git filter-repo and force-push. Future you
will not enjoy the rotation, but past-you's mistake is bounded
from here.
Step 8 — Segment IoT devices on their own VLAN
Most smart-home gear is shipped by manufacturers whose security maturity is measurably worse than a typical Linux distro. Cheap Zigbee plugs, off-brand IP cameras, Wi-Fi smart bulbs — these run vendor firmware that is rarely patched and frequently phoning home to servers you can't audit. Putting Home Assistant on the same flat LAN is asking for lateral movement.
The pattern that works:
- IoT VLAN. Every smart-home device — Wi-Fi bulbs, plugs, thermostats, cameras — on a dedicated VLAN with no route to anything except its specific cloud endpoint and the HA host.
- HA VLAN. The HA box (and its database, MQTT broker, Zigbee2MQTT/zwavejs-ui hosts) on a separate, more trusted VLAN.
- Default-deny inter-VLAN. Trust → IoT explicitly allowed for the protocols HA needs (mDNS, MQTT, HTTP to specific device IPs). IoT → Trust denied by default. IoT → Internet denied except specific NTP and vendor cloud endpoints.
- Zigbee, Z-Wave, Matter, Thread. These are physically separate radios; they don't ride on the IP network. The risk surface is the coordinator host — keep it on the HA VLAN.
pfSense, OPNsense, UniFi, and Mikrotik all do VLAN-tagged networks at the homelab price point. Spend the weekend; it pays back the first time a Wi-Fi camera firmware turns out to phone home to a server in a jurisdiction you weren't expecting.
Step 9 — Backups, encryption, and a restore drill
Home Assistant accumulates state — automations, dashboards, device registrations, historical data — that is genuinely hard to rebuild. Loss is not catastrophic the way a stolen credential is, but a corrupted SD card on a Pi after eighteen months of accreted config will ruin a weekend.
The Backups feature (Supervisor → Backups) handles the local snapshot. The parts that are easy to skip:
- Schedule it. A nightly partial backup + weekly full backup is the standard cadence.
- Encrypt it. HA backup encryption uses a password you set. Store that password somewhere you can retrieve it without HA running. A backup you can't decrypt is not a backup.
- Push it off the box. The Google Drive Backup, Samba Backup, and Nextcloud Backup add-ons all sync backups offsite. Pick one. A backup on the same SD card as the thing that failed is not a backup.
- Drill the restore. Once a quarter, spin up HA in a VM, restore the most recent backup, confirm the dashboard loads and one or two automations run. The first restore is not the time to discover your encryption password is wrong.
Step 10 — Stay current on HA Core, OS, and base images
Home Assistant Core releases approximately monthly. Major breaking changes ship on a predictable schedule; security fixes do not. HA OS, Supervisor, and the add-on base images update independently. Falling more than two releases behind on Core is the most common drift pattern.
The cadence that works at homelab scale:
- Watch release notes. Subscribe to the HA blog RSS feed. Skim every release for breaking-changes and deprecations.
- Update Core within a week. Wait 24-48 hours for community feedback on the new dot-release; then apply. Don't sit on a release for months.
- Update the underlying OS. HA OS updates ship through Supervisor. Supervised users update the host Debian via the package manager — see The patching gap on self-hosted services for the wider argument about why this matters more than the dashboard suggests.
- Rebuild custom containers. If you run HA
Container with custom Dockerfiles, rebuild monthly so base
images pick up CVE fixes.
docker pullthe base image, rebuild, redeploy.
The "set it and forget it" HA install — three years on the same Core release, OS untouched since flash — is overwhelmingly common and overwhelmingly the install that compromises catch out. HA Core has had real CVEs over the years; staying current is the only sustainable defence.
Common mistakes
- "It's only on my LAN." Compromised IoT device → lateral movement → HA token. The flat-LAN assumption breaks the moment any device in your house gets popped.
- Trusting the reverse proxy as a
trusted_networksentry. Step 2's footgun. Worth re-reading. - Letting
secrets.yamlride in a public repo. Step 7. Audit before you push, not after. - "HACS install + never look again." Custom
integrations are dependencies. Treat them with the same
scrutiny as
npm installon a production server. - Backups you've never restored. A backup whose restore process you've never executed is a hope, not a backup.
- Old long-lived tokens. Every one is a bearer credential to your house. Rotate.
Where Noxen fits in
Noxen is a Mac-native agentless scanner for the Linux hosts running your homelab — including the HA OS host, the Pi running Supervised HA, or the VM serving HA Container. It flags exposed Home Assistant admin surfaces among roughly seventy services it detects (Grafana, Portainer, Frigate, Plex, *arr, Pi-hole, Mosquitto, the lot), checks SSH and TLS hygiene on every host, and matches the installed package set against a signed daily CVE feed.
Run it on a schedule and it surfaces drift the morning after: port 8123 became reachable, a new add-on opened a host port, an SSH config edit re-enabled passwords. $79 one-time at noxen.app. For the wider rhythm see the 30-minute homelab security baseline.
Try Noxen — $79 one-time, agentless, diff-from-yesterday reports for the boxes that run your smart home.
Scan the boxes that run your smart home
Noxen runs nightly agentless audits over SSH and shows only what changed since the last scan — exposed Home Assistant or add-on UIs, new CVEs, SSH and TLS drift. Mac-native control plane, no agents on the targets.