Jump to: which install type, the Compose file, a reverse proxy, USB radios or backups and updates.
Before you start
Home Assistant is open-source home automation software. It connects to thousands of devices and services, keeps their history, and runs automations between them, all on your own hardware. This guide runs it as a Docker container, a setup the project calls Home Assistant Container, on a Linux machine that already runs other services.
You need a 64-bit Linux host that stays on, with Docker Engine and the Compose plugin; the Docker Installation Guide covers the install. A Raspberry Pi 4 or 5 with an SSD, or any small x86 machine, is plenty. Store the configuration on an SSD rather than a microSD card: the history database writes constantly, and cards wear out.
Container or Home Assistant OS?
Home Assistant offers several installation types, and the choice affects what you can do later.
| Feature | Home Assistant OS | Home Assistant Container |
|---|---|---|
| What it is | A complete operating system for a dedicated machine or VM | One container on a Linux host you manage |
| Add-ons (one-click installs of Mosquitto, ESPHome and so on) | Yes | No. You run those as separate containers. |
| Built-in backups and updates from the interface | Yes | No. You back up the config directory and pull new images yourself. |
| Shares the machine with other services | Not really | Yes |
| You maintain the host OS | No | Yes |
If you want a dedicated appliance with the least maintenance, Home Assistant OS on its own machine or in a VM, for example on Proxmox, is the better fit. Choose the container if you already run a Docker host and are comfortable managing Compose stacks and backups. Everything Home Assistant itself does, including integrations, automations and dashboards, is the same in both.
The Compose file
mkdir -p ~/stacks/homeassistant/config && cd ~/stacks/homeassistant
name: homeassistant
services:
homeassistant:
image: ghcr.io/home-assistant/home-assistant:2026.10
restart: unless-stopped
network_mode: host
privileged: false
environment:
TZ: Europe/London
volumes:
- ./config:/config
- /etc/localtime:/etc/localtime:ro
- /run/dbus:/run/dbus:ro
The settings that matter:
- The image tag. Home Assistant releases monthly, as
YYYY.MM, with patch releases such as2026.10.1in between. Pinning the monthly tag means patches arrive when you pull, and a new month's release, which can contain breaking changes, only arrives when you change the tag. Check the release notes for the current version and use it here. Avoidstableorlatestif you want to read the breaking changes before they hit you. network_mode: hostis what Home Assistant's own documentation uses. Many integrations discover devices with mDNS, SSDP or UDP broadcasts, which don't cross Docker's bridge network. With bridge networking, discovery mostly stops working. The section on networking explains the trade-off.TZsets the time zone. Use your own tz database name. A wrong time zone makes automations fire at the wrong hour./run/dbuslets the Bluetooth integration talk to the host's BlueZ service. Leave it out if the machine has no Bluetooth or you don't use it.privileged: false. Some guides setprivileged: trueto make USB devices work. Pass the specific device instead, as shown under USB radios.
docker compose up -d
docker compose logs -f homeassistant # first start takes a minute or two; Ctrl+C to stop following
When the log shows the HTTP server has started, open http://host-address:8123 from a browser on your network.
Onboarding
The first visit walks you through creating the owner account, naming your home, setting its location and choosing units. The location drives sunrise and sunset times and weather; it's stored only in your own configuration.
- Use a strong, unique password. This account can unlock doors and turn off heating if you connect those devices.
- After onboarding, open your profile (the user icon at the bottom of the sidebar) and enable multi-factor authentication with an authenticator app.
- Create separate, non-administrator accounts for other people in the house under Settings → People.
Home Assistant then lists devices it has already discovered on your network. Add the ones you recognise. You can come back to the rest under Settings → Devices & services.
Host networking and its trade-offs
With host networking, Home Assistant listens directly on the host's port 8123, and its own discovery traffic reaches the LAN. That's what makes it work well. It also means two things worth planning for:
- Your host firewall does apply. Unlike a published Docker port, which bypasses ufw (see Docker Networking Explained), a host-mode service is filtered by your normal rules. Allow 8123 from your LAN and nothing else:
sudo ufw allow from 192.168.1.0/24 to any port 8123 proto tcp. - Port conflicts are with the host. If anything else on the machine already uses 8123, Home Assistant won't start.
Don't forward port 8123 from your router to the internet. For access from outside, use a VPN such as Tailscale or WireGuard, or Home Assistant Cloud, the paid remote-access service run by the project's company.
A reverse proxy
If you put Home Assistant behind a reverse proxy for HTTPS, it refuses proxied requests until you say which proxy to trust. Without this, you get 400: Bad Request and the log shows a message about a reverse proxy not being trusted. Add to config/configuration.yaml:
http:
use_x_forwarded_for: true
trusted_proxies:
- 127.0.0.1
- ::1
List the address the proxy connects from. If the proxy runs in a bridge-networked container on the same host, that's the Docker network's gateway or subnet, for example 172.18.0.0/16; the proxy's own log shows the address Home Assistant sees. The proxy must also pass WebSocket upgrades, since the interface uses a WebSocket for everything. With Caddy that's automatic:
ha.example.net {
reverse_proxy 127.0.0.1:8123
}
Restart Home Assistant after editing configuration.yaml: docker compose restart homeassistant. Use Developer tools → YAML → Check configuration first, so a typo doesn't stop it from starting.
Keeping configuration manageable
Most setup happens in the interface and is stored in config/.storage/, which you shouldn't edit by hand. Some things, such as the http: settings above, still live in configuration.yaml. As that file grows, split it:
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
homeassistant:
packages: !include_dir_named packages
Each file in config/packages/ can then hold everything for one area or feature, such as the garage, with its sensors, automations and helpers together. Credentials go in config/secrets.yaml and are referenced with !secret, the same as in ESPHome, so the rest of the configuration can be kept in a Git repository. Add secrets.yaml, .storage/ and the database files to .gitignore.
USB radios for Zigbee and Z-Wave
Zigbee and Z-Wave devices connect through a USB radio, a coordinator stick. Pass the device into the container by its stable path, not as /dev/ttyUSB0, which can change when you plug in another USB device:
ls -l /dev/serial/by-id/
devices:
- /dev/serial/by-id/usb-Itead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_V2_20240123456789-if00-port0:/dev/ttyZigbee
Add that under the service, using the path from your own system, then docker compose up -d. In Home Assistant, add the Zigbee Home Automation integration and give it /dev/ttyZigbee as the serial port.
Plug the stick into a USB 2.0 port or a short USB extension cable, away from USB 3.0 ports and SSDs. USB 3.0 produces interference in the 2.4 GHz band that Zigbee uses, and a 50 cm cable fixes a surprising number of unreliable networks.
Companion services
Without add-ons, related services run as their own containers. The common ones each have a guide:
- MQTT broker: Mosquitto MQTT Broker Setup. Then add the MQTT integration in Home Assistant with the
homeassistantaccount. - ESPHome: ESPHome: Your First Device, including a Docker Compose file for the dashboard.
- Zigbee2MQTT as an alternative to ZHA, if you prefer your Zigbee network independent of Home Assistant. It publishes devices over MQTT.
Run each in its own Compose project, or add them to this one. Services using host networking reach each other on 127.0.0.1.
The history database
Home Assistant records every state change in a SQLite database, config/home-assistant_v2.db. Ten days of history is kept by default. On a busy installation, the file grows to several gigabytes and becomes the main source of disk writes. Two settings keep it in check:
recorder:
purge_keep_days: 14
exclude:
entity_globs:
- sensor.*_wi_fi_signal
- sensor.*_uptime
domains:
- updater
Exclude entities that change often but whose history you never look at. Long-term statistics, the hourly averages used by energy and history graphs, are stored separately and kept indefinitely, so excluding an entity from short-term history doesn't lose its long-term graph.
Backups and updates
The container install has no backup button. Everything that matters is in the config directory. Stop the container briefly so the database is consistent, and archive it:
cd ~/stacks/homeassistant
docker compose stop
sudo tar -czf ~/backups/homeassistant-$(date +%F).tar.gz -C ~/stacks/homeassistant config
docker compose start
Store a copy off the machine. The Docker Volumes and Backups guide shows how to schedule this with a systemd timer and test a restore. The archive contains secrets.yaml and access tokens, so keep it somewhere private.
To update:
- Read the release notes' Backward-incompatible changes section for the new month, looking for integrations you use.
- Take a backup.
- Change the tag in
compose.yaml, for example from2026.10to2026.11. - Run
docker compose pull && docker compose up -dand watch the log.
To roll back, stop the container, restore the config archive taken before the update, change the tag back, and start it again. Restoring the config matters: a newer version may have migrated the database, and the older one can't read it.
Troubleshooting
Devices aren't discovered
Check the container is really using host networking: docker inspect --format '{{.HostConfig.NetworkMode}}' homeassistant-homeassistant-1 should print host. Discovery also doesn't cross VLANs or subnets without an mDNS reflector on your router.
400: Bad Request through the reverse proxy
The proxy's address isn't in trusted_proxies. The log line names the address it rejected; add that address or its subnet.
The Zigbee integration can't open the serial port
The device isn't passed into the container, or the path changed. Check with docker compose exec homeassistant ls -l /dev/ttyZigbee. Also make sure no other program on the host, such as ModemManager, has claimed the port: sudo systemctl disable --now ModemManager if you don't use a mobile modem.
Home Assistant won't start after a configuration change
Read the last lines of docker compose logs homeassistant; it names the file and line. Fix it, or restore the previous version from your backup or Git history.
Next steps
- ESPHome: Your First Device: build your own sensors.
- Mosquitto MQTT Broker Setup: connect MQTT devices securely.
- Uptime Kuma Uptime Monitoring: get an alert if Home Assistant stops answering.
- The project's Linux installation page covers the container install in its own words.