Safeguard
Supply Chain Security•

Home Assistant Self-Hosting Explained: Install, Remote Access, Hardening

Run Home Assistant OS on a device or VM, keep devices local, turn on MFA and encrypted backups, and reach it remotely through a VPN, not an open port.

Safeguard Security Team
43 min read

The safest way to self-host Home Assistant is to run Home Assistant OS on a small device or in a virtual machine, keep your devices on local protocols, turn on multi-factor authentication and encrypted automatic backups, and reach it from outside through a VPN instead of an open router port. Home Assistant OS and Home Assistant Container are now the only two supported install methods; the older Core and Supervised methods lost support with release 2025.12.

This guide goes one level deeper than our posts on Zigbee, Thread and Matter, MQTT 5 and building a home firewall on a mini PC. This one covers the hub in the middle. Every setting, default and version below comes from the Home Assistant user and developer documentation (Home Assistant 2026.10.3 and Home Assistant OS 18.3 were current when we checked on 24 October 2026), the Open Home Foundation, the Proxmox VE manual and the WireGuard project. Commercial products appear only as categories.

Key takeaways

  • Home Assistant is a Python application (Core) plus, on the recommended install, an appliance operating system (Home Assistant OS) and a Supervisor that runs Core and its apps in containers, makes backups and installs every update. Add-ons are now called apps.
  • Two install methods are supported: Home Assistant OS (on a device such as a Raspberry Pi 4 or 5, an x86-64 machine, or a VM) and Home Assistant Container. Core and Supervised were deprecated in May 2025 and stopped being supported with 2025.12, together with 32-bit i386, armhf and armv7 builds.
  • Home Assistant has belonged to the Open Home Foundation, a nonprofit announced on 20 April 2024, together with more than 240 related projects at launch.
  • Integrations declare how they talk to things: local push, local polling, cloud push, cloud polling, assumed state or calculated. Prefer local ones; a local integration keeps working when your internet connection or a vendor's cloud does not.
  • Home Assistant treats every logged in user as trusted and does not enforce user privileges, and its trusted networks login skips both password and MFA. Fewer accounts, MFA on all of them, and no trusted networks.
  • By default Home Assistant is only reachable on your local network. To reach it remotely, use the paid Home Assistant Cloud relay, a VPN, or a reverse proxy with TLS. A bare port forward is the option the docs themselves call not secure.
  • Backups are encrypted by default. Download the emergency kit with the encryption key, or a backup you cannot decrypt is not a backup.
  • Custom integrations run inside Core with full access and pull pinned Python packages from PyPI at load time. Starting with 2026.11, the community store (HACS) moves into Home Assistant as the Marketplace.

What is Home Assistant, and who stewards it?

A short history

Home Assistant began in 2013, when its founder, Paulus Schoutsen, wrote a few lines of Python to control his own smart lights, according to the Open Home Foundation's history page. It grew, mostly through volunteer contributors, into a platform that runs entirely on hardware the user owns, with an integration for most smart home protocols and many cloud services.

The project's commercial arm, Nabu Casa, sells the optional Home Assistant Cloud subscription and official hardware such as Home Assistant Green. The installation page describes Green as a device that ships with Home Assistant OS installed: you connect power and Ethernet and start.

The Open Home Foundation

On Saturday, 20 April 2024, Schoutsen and three colleagues announced the Open Home Foundation. The announcement says that, ahead of that day, more than 240 projects, standards, drivers and libraries had been transferred to it, naming Home Assistant, ESPHome, Zigpy, Piper, Improv Wi-Fi and Wyoming among them. The stated aim was to protect the projects against "the risk of buyout" and against becoming abandonware, a protection the founders said extends even against their own future selves.

The foundation's About page now says it owns over 250 projects, and that through partnerships with Nabu Casa and Apollo Automation it funds external projects such as WLED, Rhasspy and Zigbee2MQTT. Schoutsen is listed as President of its board. Home Assistant's securing page now says a Home Assistant Cloud subscription supports the Open Home Foundation, "the nonprofit behind Home Assistant."

For a self-hoster, the point is practical: the code is open source, owned by a nonprofit rather than a company that could be sold, and the paid cloud service is optional.

The parts: Core, Supervisor, Home Assistant OS, apps and integrations

The developer documentation's architecture overview opens with a useful sentence: Home Assistant "is not one program." These are the pieces.

PartWhat it isWhere it runs
Home Assistant CoreThe Python application at the center. It holds the state of every device, runs automations, keeps history (the recorder) and serves the APIsEvery installation
FrontendThe web interface: dashboards, settings, the automation editor. Core serves its filesIn the browser and the companion apps
IntegrationsPython code inside Core that connects to a device, protocol or service, usually through a separate Python library. Each turns devices into entitiesInside Core
SupervisorRuns Core in a container next to apps, makes backups and installs updates of everything, including the operating systemHome Assistant OS only
Home Assistant OSA minimal embedded Linux built to run Core, the Supervisor and appsSingle board computers, x86-64 machines, VMs
Apps (formerly add-ons)Standalone software that runs next to Core, such as an MQTT broker, the Z-Wave JS server or the Matter ServerHome Assistant OS only
Companion appsPhone, tablet and desktop apps that show the frontend and add device sensors and notificationsYour devices

The key idea in the developer docs is that "everything becomes entities": a thermostat becomes a climate entity and a few sensors, and automations and dashboards then work only with entities, whatever the brand. Some protocols run as their own program beside Core: the Z-Wave JS server, an MQTT broker, the Matter Server. On Home Assistant OS each is an app from Settings > Apps; on Container you run them as more containers yourself.

A word on naming. The project has renamed add-ons to apps, and its docs now say "app (formerly known as add-on)" throughout. Older forum posts and guides still say add-on. They mean the same thing.

What are the install methods?

The two supported methods

The installation page lists exactly two installation types and calls Home Assistant OS "the recommended installation type for most users."

Home Assistant OS is the appliance. You flash an image onto a device or import it as a VM disk, boot it, and finish setup in a browser. You get apps, one click updates of Core, the OS and apps, and full backups that include apps. The page lists Home Assistant Green, a Raspberry Pi 4 or 5 with at least 2 GB of RAM, Odroid boards and generic x86-64 machines as targets, plus VM images for KVM (.qcow2), VirtualBox (.vdi) and VMware ESXi or vSphere (.ova). The VM instructions call for 2 GB of RAM and 2 vCPUs as a minimum, UEFI firmware ("Home Assistant requires UEFI to boot") and a 32 GB default disk.

Home Assistant Container is Core in a Docker container on a Linux system you already run. You manage the host, the updates and any extra services. The docs are blunt: Container installations "don't have access to apps," and some integrations that depend on apps, such as Thread and Z-Wave, have no out of the box support there. The official compose file runs the image ghcr.io/home-assistant/home-assistant:stable with privileged: true and network_mode: host, mounting /run/dbus read only. Those two settings exist so discovery, Bluetooth and USB radios work, and they also mean the container is not much of a sandbox.

Which methods were deprecated

On 22 May 2025, the project announced the deprecation of two install methods and three CPU architectures:

Date or releaseWhat happenedSource
22 May 2025Core (Python virtual environment) and Supervised (Supervisor on your own OS) install methods deprecated; i386, armhf and armv7 deprecatedHome Assistant blog
2025.6Affected systems show a notice after updating; end user docs for Core and Supervised removedHome Assistant blog
2025.12Support ends: issue reports no longer accepted for Core and Supervised; no more builds for the three 32-bit architecturesHome Assistant blog

The post gives the numbers: Core and Supervised were 2.5% and 3.3% of installations, and each 32-bit architecture was around or under 1%, with more than half of armv7 systems on hardware that can run a 64-bit OS. Core remains the developer setup, and nothing stops you running an unsupported install, but you are on your own when it breaks.

To check what you run, open Settings > System > Repairs, open the three dot menu, select System information, and read the Installation type and CPU architecture fields. Anything other than Home Assistant OS or Container, or an architecture other than aarch64 or x86_64, needs a migration plan.

Which method should you pick?

You havePickWhyWhat you give up
No server, want it to just workHome Assistant OS on a dedicated deviceAppliance updates, apps, full backupsShell level control of the host
A hypervisor such as Proxmox VEHome Assistant OS in a VMSame appliance, plus VM snapshots and easy hardware movesUSB radio passthrough needs care
A Linux host already running DockerHome Assistant ContainerOne more container in an existing stackApps, Supervisor backups of apps, one click OS updates
A 32-bit boardNew hardware or a 64-bit OS32-bit builds stopped with 2025.12Nothing worth keeping

For most homelabs, Home Assistant OS in a VM is the sweet spot: the supported appliance with all apps, plus hypervisor snapshots and a VLAN tag on the virtual network card. The walkthrough below builds exactly that.

How do integrations reach devices?

The six IoT classes

Every integration's manifest.json declares an iot_class, and the integrations page shows it as a badge. The developer docs define the classes:

IoT classMeaningWorks without internet?Typical example
local_pushThe device sends updates to Home Assistant on the local network as they happenYesZigbee and Z-Wave through a local radio, MQTT
local_pollingHome Assistant asks the device for its state on an intervalYesMany LAN devices with an HTTP API
cloud_pushThe vendor cloud pushes updates to Home AssistantNoDevices that only expose a vendor cloud API with webhooks
cloud_pollingHome Assistant asks the vendor cloud on an intervalNoWeather, energy and many vendor accounts
assumed_stateHome Assistant cannot read the state and assumes the last command workedDependsSimple infrared or 433 MHz senders
calculatedTalks to nothing; computes a resultYesTemplates, statistics

Polling has limits. The common tasks docs say many integrations update at most once every 10 seconds. You can turn off an integration's built in polling under its System options and drive the Update entity action from your own automation, which helps with rate limited cloud APIs.

The radios: Zigbee, Z-Wave, Thread and Matter

Zigbee. The Zigbee Home Automation (ZHA) integration talks to a USB coordinator plugged into the Home Assistant machine through the zigpy library. A Zigbee network has exactly one coordinator; routers and end devices join it. An alternative many people use is Zigbee2MQTT, which runs as its own service and publishes devices over MQTT; the Open Home Foundation funds its development.

Z-Wave. The Z-Wave integration uses the Z-Wave JS driver, run as a server (the Z-Wave JS app on Home Assistant OS) that the integration connects to. The app holds the network's security keys, and the integration docs have an FAQ on where to see them. Keep a copy with your other secrets, and make sure the app is in your backups.

Matter. The Matter integration runs its controller as a separate Matter Server app and talks to it over a WebSocket. Matter has no radio of its own: it runs over IPv6 on Wi-Fi, Ethernet or Thread. Commissioning uses Bluetooth from the companion app on your phone. Each controller has its own fabric, and the docs say every device should support at least five fabrics, so one device can join Home Assistant and another ecosystem together.

Thread. Thread is a low power IPv6 mesh. A Thread border router bridges the mesh to your LAN. The Matter docs note that traffic between Home Assistant's Matter controller and a device is encrypted, and that the border router passing it along "cannot read its content."

The Matter page carries a warning that shapes network design: Matter relies on local IPv6 and mDNS multicast, and "may not integrate well with enterprise networking solutions like VLANs," multicast filtering and broken IGMP snooping. It asks that Wi-Fi Matter devices and Thread border routers be on the same VLAN as Home Assistant. We follow that advice in the walkthrough: Home Assistant moves into the IoT VLAN with its devices, rather than sitting on the trusted LAN and reaching across a router.

Our Zigbee, Thread and Matter post covers the link layer security of each protocol in depth.

MQTT

The MQTT integration connects Home Assistant to a broker, and the docs recommend the official Mosquitto broker app. When you set up the integration with that app, Home Assistant generates a username and password for itself automatically. The docs say public brokers exist but "the easiest and most private option is running your own," and they list some broker products that do not work correctly with Home Assistant's use of retained messages. Give each device or bridge its own broker login rather than sharing one. Our MQTT 5 post covers ACLs, TLS on port 8883 and session handling.

Why local control matters for privacy and uptime

Home Assistant's own securing page starts from this point: because it runs on your hardware and "does not depend on any cloud service to work," a large class of cloud platform risks does not apply. The remote access page adds that by default Home Assistant is only reachable from your local network.

In practice, a local_push Zigbee motion sensor turns on a light during an internet outage; a cloud_polling plug does not, and stops working for good if its vendor shuts the service down. Local events also never leave the house, and local push is faster than any poll interval. Read the IoT class badge on the integration page before you buy a device.

How does authentication work?

Users, owners and administrators

The first user created during onboarding becomes the owner, who can create other users under Settings > People > Users. The default provider stores passwords salted and hashed in the .storage directory. Lose the owner's password and the docs say you start a new onboarding.

Each user can be marked as an administrator or not. Treat that flag as a convenience for hiding settings screens, not a security boundary. Home Assistant's security policy is explicit: it does not accept privilege escalation reports for logged in users, because "Home Assistant assumes every user is trusted and does not enforce user privileges." A non-admin account that can log in can, in effect, do what an owner can through the APIs. Only create accounts for people you would trust with the whole house, and use separate tokens, not separate users, to scope what scripts do.

Refresh tokens and long-lived access tokens

Each sign in from a device creates a refresh token. From User profile > Security you can see them and delete one to force that device to sign out. Unused refresh tokens are removed automatically once they have not been used to sign in for 90 days.

For scripts and other systems, create a long-lived access token on the same page. Clients send it as an Authorization: Bearer header to the REST or WebSocket API. A long-lived token carries the full rights of the user who made it and does not expire on its own schedule, so store it like a password, give each consumer its own token, and delete tokens you no longer use.

Multi-factor authentication

Home Assistant ships two MFA modules:

  • TOTP (authenticator app). If configuration.yaml has no auth_mfa_modules section, a TOTP module named Authenticator app is loaded automatically. You enable it per user under User profile > Security by scanning a QR code into any authenticator app. Codes change about every 30 seconds and depend on an accurate clock on the Home Assistant host.
  • Notify. Sends an HMAC-based one-time password through a notify action, usually to the companion app. The docs warn that the module cannot tell whether the code was delivered; if the notification does not arrive, you cannot log in.

MFA works with any login provider except one: trusted networks.

Login providers, including trusted networks

There are three built in authentication providers:

ProviderWhat it doesRisk
homeassistantUsername and password stored locally; the defaultNone beyond password strength; pair it with MFA
trusted_networksSkips the password for listed IP ranges; the user just picks an accountMFA does not run; anyone on that network can log in by choosing a name
command_lineRuns a shell command with the username and password in environment variables; exit code 0 grants accessYour script's quality is your login security

The docs carry two warnings about auth_providers. Adding the block disables every provider not listed in it, so a typo can lock you out with no way back through the UI. And using only trusted_networks without type: homeassistant as a fallback means "anyone on your trusted network can log in by selecting a username." Providers are evaluated top to bottom. Also note that you cannot trust a network that is listed in your trusted proxies; login from it fails.

Our advice: skip trusted networks. If you must use it, scope it to one wall tablet's address with trusted_users, keep type: homeassistant in the list, and never include the subnet your proxy or VPN clients arrive from.

IP banning and the HTTP server settings

The HTTP integration, which serves the frontend and APIs, is now configured in the UI under Settings > System > Network, HTTP server. Starting with Home Assistant 2026.8, new Home Assistant OS installs listen on port 80 by default; Container keeps 8123, and Home Assistant OS installs set up before 2026.8 keep the port they had. Saving HTTP settings restarts Home Assistant and asks an administrator to confirm; if nobody confirms within 5 minutes, the old settings return, which protects you from locking yourself out.

The same screen controls IP banning. Turn on IP banning and set the number of failed logins before a ban, from 1 to 1000. Banned addresses go into ip_bans.yaml in the config folder with a UTC timestamp, a notification appears, and bans never expire on their own; you remove the line or delete the file and restart.

One more detail from the HTTP docs that catches people: files in the www folder are served at /local/ and "aren't protected by the Home Assistant authentication." Never put anything private there.

How do you reach it remotely without exposing it raw?

The four options

The remote access page lists four routes and recommends checking the securing checklist before any of them.

Home Assistant Cloud. A paid subscription from Nabu Casa. The docs say it needs no open router ports, generates a unique remote URL and encrypts traffic between your device and your home; setup is a toggle. The developer architecture page describes the same service, under the name Home Assistant Link, as an encrypted tunnel that also connects voice assistants and offers speech services. Your subscription funds the project, and the cloud backup feature stores one encrypted backup of up to 5 GB.

A VPN. You connect your phone or laptop to your home network first, then open Home Assistant as if you were at home. The docs call this "a secure way" to reach it and note the trade: when the VPN is down, the companion app cannot send sensor updates. You can run your own WireGuard endpoint on your router or firewall, or use a hosted mesh VPN service.

A reverse proxy with TLS. A proxy on your network terminates HTTPS on a public port and forwards to Home Assistant. You must turn on Trust X-Forwarded-For and add the proxy's address or network to Trusted proxies, or requests are blocked "by design." The docs add caching rules for proxies and CDNs: never cache HTML, the service worker files, 404s, or anything under /api/ or /auth/.

A plain port forward. Forward a router port to Home Assistant and use dynamic DNS. The docs warn that "just putting a port up is not secure," and that with carrier grade NAT, now common, inbound ports cannot work without a paid dedicated IPv4 address.

Remote logins must use a domain name, not a raw public IP. Home Assistant only accepts an IP address as an OAuth client ID when it is a private or loopback address, so a public IP produces an "invalid client id or redirect url" error.

Comparison: remote access methods

MethodWhat is exposed to the internetSetup effortOngoing costMain failure modes
WireGuard VPN you runOne UDP port that answers only to known keysMedium: keys, a router rule, a client per deviceFreeCG-NAT blocks inbound; lost or shared client key; app sensors stall when the tunnel is down
Hosted mesh VPN serviceNothing inbound; devices dial out to a coordination serviceLowFree tier or subscriptionDependence on the provider's control plane and account security
Home Assistant Cloud relayNothing inbound; Home Assistant dials outLowest: one togglePaid subscriptionAccount takeover of your Home Assistant login; service outage cuts remote access
Reverse proxy with TLSHTTPS on a public port, with the Home Assistant login page reachable by anyoneMedium to high: DNS, certificates, proxy config, trusted proxiesDomain costMisconfigured trusted proxies; expired certificates; login page brute force; any future web vulnerability is internet facing
Plain port forwardHome Assistant itself, often without TLSLowFreeCredentials in clear text without TLS; full web attack surface exposed; CG-NAT

For a homelab, the VPN gives the smallest exposure: WireGuard does not answer packets from unknown peers, so a port scan sees nothing useful. The cloud relay is easiest and also opens no inbound port. Choose the reverse proxy only when people without VPN clients must reach the login page, and then pair it with IP banning, MFA for every user and a proxy you keep patched.

Our post on whether it is safe to self-host apps on the internet walks through the same trade-offs for any self hosted service.

How do backups, updates and the add-on supply chain work?

Backups

Home Assistant's backup system covers all supported install methods, and you can restore a backup from one method onto another, even across CPU architectures, according to the 2025 deprecation post. The backups page states the defaults:

  • Backups are encrypted by default and stored as compressed .tar archives.
  • The recommended automatic schedule backs up everything daily except media, the share folder and the local apps folder, and keeps the last 3 backups. It stores them on the system itself, and on Home Assistant Cloud if you are logged in.
  • You can add network storage with the Backup usage as a location, and set encryption and retention per location. Retention applies to automatic backups only; manual backups are not deleted.
  • Home Assistant Cloud stores a single backup of up to 5 GB, always encrypted, and replaces it with each upload.
  • On Container, a backup holds settings and optionally history; apps, media, share and network storage are Home Assistant OS features.
  • On Home Assistant OS, a Backup preference when updating setting creates a backup before each update, and app updates keep one backup per app.
  • The backup.create_automatic action lets you build your own schedule, for example waking a NAS before backing up, and the Backup integration exposes sensors and an event entity so you can alert on failures.

The one step people skip is the emergency kit. During setup you download a file containing the encryption key. Without that key you cannot restore an encrypted backup. Keep it in a password manager and on paper, not on the Home Assistant machine. Our post on building a tiny data center at home covers where that network storage can live.

Updates and release cadence

Home Assistant ships a major release every month, named by year and month (2026.10 is the October 2026 release), followed by patch releases such as 2026.10.3. The securing checklist says to keep your system up to date "with each monthly release." Home Assistant OS has its own version line (18.3 at the time of writing), and the Supervisor and each app update separately; on Home Assistant OS, all of them appear under Settings > System > Updates.

Read the release notes before a major update; each has a Backward-incompatible changes section (2026.10, for example, was published on 7 October 2026). A sensible routine: wait for the first patch after a .0 release, make sure the pre-update backup is on, update, then check Settings > System > Repairs.

Security fixes are published as GitHub Security Advisories and listed on the project's security page. The policy asks reporters for 90 days before disclosure, and the project requests CVEs for valid issues of medium severity or higher in Home Assistant itself. The page excludes attacks that require installing malicious third party software, such as a custom integration or app, from its scope. That exclusion matters for the next section.

Apps, custom integrations and the community store

There are three ways to add code to Home Assistant, and each has a different trust model:

Code sourceWho reviews itHow it runsUpdate path
Built in integrationsHome Assistant maintainers, in the core repositoryInside CoreWith each Home Assistant release
Official appsHome Assistant projectSeparate containers managed by the SupervisorApp updates in the UI
Third party app repositoriesThe repository ownerSeparate containers managed by the SupervisorWhenever the owner publishes
Custom integrations (HACS or manual)The repository owner; the community store checks structure, not intentInside Core, with the same access as built in codeWhenever the owner publishes

A custom integration is Python that loads into the Core process. The manifest docs say an integration lists its Python requirements "in pip format and pinned to an exact version," and Home Assistant installs them before loading it. Each custom integration can therefore bring its own PyPI dependency tree into the process that holds your tokens, device credentials and history.

HACS, the Home Assistant Community Store, has been the usual way to find and update custom integrations, dashboard cards and themes. Its docs now say that starting with Home Assistant 2026.11, HACS is built into Home Assistant as the Marketplace under Settings > Marketplace. The catalog stays the same (hacs/default), installed items keep updating, custom repositories carry over, the HACS custom integration is blocked on 2026.11 and newer, and AppDaemon apps and Python scripts stop being managed. Since 2026.11 is not released as we write, check the release notes when it lands.

The practical rules for this supply chain:

  1. Install as few custom integrations as you can. Prefer a built in integration, even if it has fewer features.
  2. Before installing, read the repository: who maintains it, how recently it was updated, which requirements it pins in manifest.json, and whether it calls home.
  3. Pin to releases rather than a default branch, and read the changelog before each update, the same way you would for any dependency.
  4. Remove integrations you no longer use; their code still loads.
  5. Back up before each custom integration update so a bad release is one restore away.

Step by step: Home Assistant OS in a Proxmox VM, on an IoT VLAN, with MFA, encrypted backups and WireGuard

This walkthrough assumes a Proxmox VE host, a router or firewall that supports VLANs and WireGuard (for example the mini PC from our home firewall post), and a managed switch. The example network plan:

NetworkVLANSubnetWhat lives there
Trusted LAN10192.168.10.0/24Laptops, phones, the Proxmox management interface
IoT30192.168.30.0/24Home Assistant, Wi-Fi smart devices, Thread border routers, Matter Wi-Fi devices
WireGuardnone10.66.0.0/24Remote phones and laptops

Home Assistant goes into the IoT VLAN because the Matter docs ask for Matter devices and border routers to share its VLAN. The firewall then lets the trusted LAN and WireGuard clients reach Home Assistant's web port, and blocks the IoT VLAN from starting connections into the trusted LAN.

Step 1: make the Proxmox bridge VLAN aware

Warning: editing the host's network configuration can cut you off from the Proxmox web UI and SSH. Do this from the physical console or with out of band access, and keep a copy of the working file.

Check which bridge and physical port you use first:

cp /etc/network/interfaces /root/interfaces.backup
ip -br link show
cat /etc/network/interfaces

Then set the bridge to be VLAN aware. The Proxmox network documentation shows the bridge-vlan-aware yes and bridge-vids 2-4094 options:

auto vmbr0
iface vmbr0 inet static
    address 192.168.10.5/24
    gateway 192.168.10.1
    bridge-ports eno1
    bridge-stp off
    bridge-fd 0
    bridge-vlan-aware yes
    bridge-vids 2-4094

Apply it with ifreload -a and confirm the web UI still loads. The switch port that feeds the Proxmox host must carry VLAN 30 tagged.

Step 2: download and verify the Home Assistant OS image

The Linux install page links the KVM image from the project's GitHub releases. Use the current version number from that page:

cd /var/lib/vz/template
wget https://github.com/home-assistant/operating-system/releases/download/18.3/haos_ova-18.3.qcow2.xz
xz -d haos_ova-18.3.qcow2.xz
qemu-img info haos_ova-18.3.qcow2

qemu-img info should report a qcow2 file with a virtual size; if it errors, the download is damaged.

Step 3: create the VM with UEFI and Secure Boot keys off

Home Assistant OS requires UEFI, and the KVM instructions on the Linux install page say to pick an OVMF firmware build without Secure Boot. In Proxmox the EFI disk option pre-enrolled-keys controls that, and its default is 0 (no pre-enrolled keys), which is what you want. Choose a free VM ID first:

qm list                      # confirm 120 is not already used
pvesm status                 # confirm the storage name, here local-lvm

qm create 120 --name haos --ostype l26 --machine q35 --bios ovmf \
  --cores 2 --memory 4096 \
  --efidisk0 local-lvm:1,efitype=4m,pre-enrolled-keys=0 \
  --scsihw virtio-scsi-pci \
  --scsi0 local-lvm:0,import-from=/var/lib/vz/template/haos_ova-18.3.qcow2 \
  --boot order=scsi0 \
  --net0 virtio,bridge=vmbr0,tag=30 \
  --onboot 1

qm resize 120 scsi0 32G

The tag=30 on net0 puts the VM's only network card in the IoT VLAN. 4 GB of RAM and 2 cores is comfortable; the docs' minimum is 2 GB and 2 vCPUs. 32 GB matches the docs' default disk size.

Step 4: pass through a USB radio (optional)

If you use a Zigbee, Z-Wave or Thread USB adapter, pass it through by its USB vendor and product ID so it survives port changes. Identify it before assigning it, because passing the wrong device can take a disk or keyboard away from the host:

lsusb
# Example output line:
# Bus 003 Device 003: ID 1a86:55d4 ... Zigbee 3.0 USB Dongle
qm set 120 --usb0 host=1a86:55d4

Double check the ID against the adapter's label or dmesg output after plugging it in.

Step 5: start, onboard and pin the address

qm start 120
qm status 120

The VM console in the Proxmox web UI shows the address it got from DHCP on VLAN 30. Give it a DHCP reservation on your router (for example 192.168.30.20), then open http://192.168.30.20 from the trusted LAN. New Home Assistant OS installs since 2026.8 listen on port 80; older installs and Container use 8123. Create the owner account with a long unique password from a password manager.

Step 6: firewall the VLANs

On the router, allow only what Home Assistant needs. The exact syntax depends on your firewall; here it is as nftables rules on a Linux router, where vlan10, vlan30 and wg0 are the interface names:

table inet filter {
  chain forward {
    type filter hook forward priority 0; policy drop;
    ct state established,related accept

    # Trusted LAN and VPN clients may open Home Assistant's web port
    iifname { "vlan10", "wg0" } ip daddr 192.168.30.20 tcp dport { 80, 8123 } accept

    # Trusted LAN may reach anything in IoT (optional, for device setup)
    iifname "vlan10" oifname "vlan30" accept

    # IoT may reach the internet, but never start connections into the trusted LAN
    iifname "vlan30" oifname "wan" accept
    iifname "vlan30" oifname "vlan10" drop
  }
}

If your devices are all local, you can go further and drop vlan30 to wan except for Home Assistant itself, which needs the internet for updates. Test that a laptop on VLAN 10 can load the UI and that a device on VLAN 30 cannot reach a laptop.

Step 7: turn on MFA for every user

  1. In Home Assistant, open your user profile (bottom left), then the Security tab.
  2. Under Multi-factor authentication modules, select Enable next to Authenticator app.
  3. Scan the QR code with an authenticator app (or type the key shown under it), then enter the current six digit code. Treat that key like a password.
  4. Log out and back in to prove it works.
  5. Repeat for every other user, and delete accounts nobody uses.

No YAML is needed: the TOTP module loads automatically when auth_mfa_modules is absent. If you add the section yourself, keep TOTP in it:

homeassistant:
  auth_mfa_modules:
    - type: totp

While you are in Security, delete refresh tokens for devices you do not recognize, and turn on IP banning under Settings > System > Network with a low threshold such as 5.

Step 8: set up encrypted automatic backups to a second machine

  1. Open Settings > System > Storage and add a network storage share from your NAS with the usage set to Backup.
  2. Open Settings > System > Backups and start the automatic backup setup.
  3. Under Download emergency kit, select Download and store the file in your password manager. This holds the encryption key.
  4. Choose a daily schedule and a retention of 7 backups.
  5. Under Locations, turn on both this system and the network storage; leave encryption on for each.
  6. Turn on Backup preference when updating so updates take a backup first.
  7. Select Back up now and confirm the file appears on the NAS.

Then take a Proxmox snapshot as a second, independent safety net before big upgrades:

qm snapshot 120 pre-2026-11 --description "Before 2026.11 update"
qm listsnapshot 120

A snapshot is not a backup: it lives on the same storage as the VM. The Home Assistant backup on the NAS is the copy that survives a dead host.

Step 9: add a WireGuard endpoint on the router

The WireGuard quickstart shows key generation with wg genkey and wg pubkey. On the router:

umask 077
wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub
wg genkey | tee /etc/wireguard/phone.key | wg pubkey > /etc/wireguard/phone.pub

Server configuration, /etc/wireguard/wg0.conf:

[Interface]
Address = 10.66.0.1/24
ListenPort = 51820
PrivateKey = <contents of server.key>

[Peer]
# phone
PublicKey = <contents of phone.pub>
AllowedIPs = 10.66.0.2/32

Phone configuration, imported into the WireGuard app (a QR code made with a tool such as qrencode is easiest):

[Interface]
Address = 10.66.0.2/32
PrivateKey = <contents of phone.key>
DNS = 192.168.10.1

[Peer]
PublicKey = <contents of server.pub>
Endpoint = home.example.net:51820
AllowedIPs = 192.168.30.20/32
PersistentKeepalive = 25

AllowedIPs = 192.168.30.20/32 sends only Home Assistant traffic through the tunnel, a split tunnel. Widen it to your subnets if you want more. The quickstart explains that PersistentKeepalive keeps NAT mappings open and suggests 25 seconds as an interval that works with most firewalls; it is off by default.

Bring it up and make it start at boot:

wg-quick up wg0
systemctl enable wg-quick@wg0
wg show

Forward UDP 51820 from the internet to the router (it usually is the router, so just allow it on the WAN interface), and nothing else. If your ISP uses carrier grade NAT, an inbound port will not work; use a hosted mesh VPN or the cloud relay instead.

Step 10: tell Home Assistant its URLs and test from outside

Under Settings > System > Network, in the Home Assistant URL section, leave the Internet field empty, since remote access goes through the VPN and the app should use the same internal address everywhere. In the companion app, set the server address to http://192.168.30.20 so it works the same at home and through the tunnel. Turn off Wi-Fi on your phone, enable the WireGuard tunnel, and open Home Assistant. Then disable the tunnel and confirm the app cannot reach it: that failure is the point.

Step 11: prove the restore

Create a temporary VM from the same image (for example ID 121, on an isolated test VLAN so it does not fight the real one for devices). During onboarding, choose to restore from a backup, upload the newest file from the NAS and enter the emergency kit key. When your dashboards and automations appear, delete the test VM. Repeat after any big change.

Common mistakes

  • Leaving trusted networks on for the whole LAN. It skips the password and MFA for anyone there, including a compromised device.
  • Assuming non-admin users are restricted. Home Assistant does not enforce user privileges for logged in users.
  • Forwarding port 8123 or 80 straight to Home Assistant. The docs say a bare port is not secure.
  • Setting Trust X-Forwarded-For without restricting Trusted proxies to the proxy's address. Keep the trusted list to the proxy host itself, never a whole subnet of clients.
  • Losing the backup emergency kit. Encrypted backups are useless without its key.
  • Backing up only to the same machine. A local backup dies with the disk.
  • Putting private files in /config/www. Anything there is served at /local/ without authentication.
  • Splitting Matter devices and Home Assistant across VLANs. Multicast filtering and IGMP snooping then break discovery.
  • Installing custom integrations without reading them. They load into Core with full access.
  • Updating without reading backward-incompatible changes. Turn on the pre-update backup too.

FAQ

Is Home Assistant free?

Yes. It is open source and owned by the Open Home Foundation, a nonprofit; you pay only for hardware. Home Assistant Cloud from Nabu Casa is an optional paid subscription for remote access, voice assistant links and off-site backup.

What is the difference between Home Assistant OS and Home Assistant Container?

Home Assistant OS is an appliance with the Supervisor, apps, one click updates and full backups. Container is Core in Docker on a host you manage, with no apps. The docs recommend Home Assistant OS for most users.

Can I still install Home Assistant Core or Supervised?

You can, but they are unsupported. The project deprecated both on 22 May 2025 and ended support with release 2025.12. Issue reports for them are no longer accepted, and their end user documentation was removed.

Does Home Assistant work without the internet?

Yes, for any integration with a local IoT class. Zigbee, Z-Wave, Matter, Thread, MQTT and many LAN devices keep working offline. Cloud polling and cloud push integrations stop until the connection returns.

What is the safest way to access Home Assistant remotely?

A VPN you control, such as WireGuard on your router, exposes one UDP port that ignores anyone without a key. Home Assistant Cloud opens no inbound ports and is the easiest. A TLS reverse proxy puts the login page on the internet, so pair it with MFA and IP banning.

How do I enable two-factor authentication in Home Assistant?

Open your user profile, go to the Security tab, and enable Authenticator app under Multi-factor authentication modules. Scan the QR code with an authenticator app and enter the six digit code. Each user enables it for their own account.

Are Home Assistant backups encrypted?

Yes, by default. The key is in the backup emergency kit you download during setup, and you need it to restore. Backups stored on Home Assistant Cloud are always encrypted. When you download a backup from the UI, it is decrypted on the fly unless you choose the encrypted download.

Is HACS safe to use?

HACS is a catalog and updater for community code, not a security review. Custom integrations run inside Home Assistant with full access and pull their own pinned Python packages, so use few, read each repository and back up before updates. From 2026.11, HACS becomes the built in Marketplace.

What port does Home Assistant use?

Since 2026.8, new Home Assistant OS installs default to port 80. Home Assistant Container still defaults to 8123, and Home Assistant OS installs set up before 2026.8 keep their old port, usually 8123. Check Settings > System > Network under HTTP server.

Should Home Assistant be on its own VLAN?

Put it on the same VLAN as its IoT devices, not apart from them. The Matter docs say Matter relies on IPv6 multicast and mDNS and ask that Matter Wi-Fi devices and Thread border routers share Home Assistant's VLAN. Use firewall rules to let your trusted network in and keep the IoT VLAN out of your trusted network.

Where Safeguard fits

Safeguard does not secure your smart home devices, router or Home Assistant login. It is a software supply chain security platform, and its fit here is indirect. Safeguard can scan software you build or pull: safeguard scan --dir . on a custom integration you maintain lists its dependencies and matches them against vulnerability databases, and safeguard scan --image ghcr.io/home-assistant/home-assistant:stable does the same for the Container image you run. That tells you about known vulnerabilities in those components. It does not inspect your devices, radios or network.

Software supply chain hygiene still matters for a smart home hub, because Home Assistant is mostly open source: Core's Python dependencies, the images that run its apps and every custom integration's pinned packages. When a vulnerability lands in one of them, the approaches below cover the parts you build and ship yourself.

Fix it without waiting for upstream

Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.

1. Autonomous remediation: pull requests, not tickets

When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.

2. Gold Open Source: the safe version in seconds, free

gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.

3. Gold Registry: start from components that are already fixed

registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.

When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingThe Gold scan GitHub Action in CI, plus Safeguard policies and gates
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch (early access)
You want to start clean on new projectsPull hardened components and images from Gold Registry

One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.

See how Safeguard would help

These demos show Safeguard working on software projects, not on smart home devices. They apply to a custom integration you maintain or a small service beside your hub.

The first demo is a real Safeguard CLI session. After install, safeguard opens the interactive terminal, you ask it to fix the lodash CVE in a repository, and it reads the finding and advisory, asks before each command, edits package.json to lodash 4.17.21, runs npm install and npm test with your approval, prints a summary and pushes nothing. The same ask, approve and test loop applies to a custom integration repository whose pinned requirements need a bump.

Click through it at your own pace, or open it full screen.

The second demo sets up the Safeguard runner on your own machine, approves it with its device code, then scans from the terminal. In a homelab, that machine can be a utility VM, so scans of your repositories and images run on hardware you control.

Click through it at your own pace, or open it full screen.

The third demo is a real Android session: you send work to a computer running the CLI and approve each command on the phone with Allow once, Allow for turn or Deny. It is the same habit this guide recommends for Home Assistant itself: nothing changes on the machine without a person saying yes.

Click through it at your own pace, or open it full screen.

Get the apps at safeguard.sh/download and read the CLI docs at docs.safeguard.sh/docs/cli.

Quick check

Which login provider skips both the password and multi-factor authentication?

Trusted networks. Anyone connecting from a listed IP range picks a user and is logged in, and the MFA module does not take part. Keep type: homeassistant as a fallback and scope trusted networks tightly, or do not use it.

Why does this guide put Home Assistant on the IoT VLAN instead of the trusted LAN?

Because the Matter integration relies on local IPv6 multicast and mDNS, and its docs ask that Matter Wi-Fi devices and Thread border routers be on the same VLAN as Home Assistant. Firewall rules then let the trusted LAN and VPN clients in while blocking the IoT VLAN from reaching the trusted LAN.

Sources

Last reviewed: 24 October 2026. Home Assistant ships monthly and renames features often; check the linked documentation for the release you run, and keep console access when changing host networking.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.