# Home Assistant Operating System — User Guide

## 1. Quick Start

Home Assistant Operating System (HAOS) is a purpose-built Linux distribution designed to run Home Assistant on dedicated hardware or virtual machines. Built on Buildroot, it provides a minimal, managed OS with automatic over-the-air (OTA) updates, A/B boot slot redundancy, and a containerized Home Assistant stack — all pre-configured and ready to run.

**Key features:**

- Plug-and-play installation — flash an image, boot, and Home Assistant is running
- A/B partition scheme with automatic rollback on failed updates
- OTA updates via RAUC with cryptographic signature verification
- Docker-based Home Assistant Supervisor, Core, and add-ons
- NetworkManager for wired, Wi-Fi, and VLAN networking
- Swap and zRAM management with configurable sizes
- Broad hardware support: Raspberry Pi, ODROID, x86-64 PCs, VMs, and dedicated appliances
- AppArmor security profiles for containerized services

**Prerequisites:**

- Supported hardware or virtualization platform (see Installation)
- SD card, eMMC, USB drive, SSD, or virtual disk (32 GB minimum recommended)
- Internet connection for initial setup and container image downloads

**Fastest path to running:**

1. Download the correct image for your hardware from the [Home Assistant installation page](https://www.home-assistant.io/installation/)
2. Flash the image to your boot media using [Balena Etcher](https://etcher.balena.io/) or similar
3. Insert the media, power on the device, and wait approximately 5–20 minutes for first boot
4. Open `http://homeassistant.local:8123` in a browser

> [!TIP]
> On first boot, HAOS expands the data partition to fill the entire disk, downloads the latest Supervisor and Home Assistant Core containers, and starts all services. The landing page at port 8123 appears once Home Assistant Core is ready.

**What to explore next:**

- Configuring your network (see Configuration below)
- Updating the OS (see Core Usage below)
- Troubleshooting connectivity issues (see Troubleshooting below)

---

## 2. Installation

### Supported Platforms

| Platform | Board / Target | Image Type | Boot Media |
|---|---|---|---|
| Raspberry Pi 3 (64-bit) | `rpi3-64` | `.img.xz` | SD card |
| Raspberry Pi 4 (64-bit) | `rpi4-64` | `.img.xz` | SD card, USB SSD, NVMe (via HAT) |
| Raspberry Pi 5 (64-bit) | `rpi5-64` | `.img.xz` | SD card, NVMe |
| Home Assistant Yellow | `yellow` | `.img.xz` | eMMC (CM4/CM5 based) |
| NabuCasa Green | `green` | `.img.xz` | eMMC |
| ODROID-C2 | `odroid_c2` | `.img.xz` | SD card, eMMC |
| ODROID-C4 | `odroid_c4` | `.img.xz` | SD card, eMMC |
| ODROID-M1 | `odroid_m1` | `.img.xz` | SD card, eMMC, NVMe |
| ODROID-M1S | `odroid_m1s` | `.img.xz` | SD card, eMMC |
| ODROID-N2/N2+ | `odroid_n2` | `.img.xz` | SD card, eMMC |
| Khadas VIM3 | `khadas_vim3` | `.img.xz` | SD card, eMMC |
| Generic x86-64 (UEFI) | `generic-x86-64` | `.img.xz` | USB, SSD, NVMe |
| Generic AArch64 (UEFI) | `generic-aarch64` | `.img.xz` | USB, SSD |
| VirtualBox / VMware / KVM | `ova` | `.ova` | Virtual disk |

> [!IMPORTANT]
> Always use the image that matches your exact hardware. Using the wrong image (e.g., `rpi4-64` on a Raspberry Pi 5) will fail to boot. The Raspberry Pi 5 requires the `rpi5-64` image specifically.

### Flashing the Image

#### SD Card / USB / SSD (`.img.xz`)

1. Download the `.img.xz` image for your platform
2. Flash using Balena Etcher, `dd`, or Raspberry Pi Imager:

```bash
xzcat haos_rpi4-64.img.xz | sudo dd of=/dev/sdX bs=4M status=progress
sync
```

```text
1887+1 records in
1887+1 records out
7918845952 bytes (7.9 GB, 7.4 GiB) copied, 142.3 s, 55.6 MB/s
```

3. Insert the media into your device and power on

#### Virtual Machine (`.ova`)

1. Download the `.ova` file
2. Import into VirtualBox, VMware, or Proxmox:
   - VirtualBox: File → Import Appliance → select the `.ova`
   - VMware: File → Open → select the `.ova`
   - Proxmox: use `qm importovf` or convert to `qcow2`
3. Allocate at least 2 GB RAM and 32 GB disk
4. Start the VM

> [!WARNING]
> For VirtualBox and Proxmox VMs, Bluetooth passthrough is not supported natively. If you need Bluetooth for Zigbee or Z-Wave dongles, use USB passthrough for the physical adapter instead. See GitHub issue [#2142](https://github.com/home-assistant/operating-system/issues/2142).

#### Generic x86-64 (Bare Metal UEFI)

1. Flash the `generic-x86-64` image to a USB drive
2. Boot from the USB drive (configure BIOS/UEFI boot order accordingly)
3. HAOS installs to the internal disk and boots from it on subsequent startups

### Post-Install Verification

After the device boots and the initial setup completes, verify the installation:

1. Open `http://homeassistant.local:8123` in a browser (or use the device's IP address)
2. The Home Assistant onboarding wizard should appear
3. From the Home Assistant CLI (via SSH add-on or console), confirm the OS:

```bash
ha os info
```

```text
version: 14.2
version_latest: 14.2
update_available: false
board: rpi4-64
boot: slot0
data_disk: /dev/mmcblk0p8
```

> [!TIP]
> If `homeassistant.local` does not resolve, use your router's DHCP client list to find the device's IP address, or connect a monitor and keyboard to see the console output showing the IP.

---

## 3. Configuration

HAOS is designed to require minimal user configuration. Most settings are managed through the Home Assistant Supervisor UI or CLI. The OS itself exposes a limited set of configuration options through specific files and paths.

### Network Configuration

Networking is managed by **NetworkManager**. Configuration can be done via:

- The Home Assistant UI: Settings → System → Network
- The `ha` CLI: `ha network info`, `ha network update`
- NetworkManager connection files on the config partition

To pre-configure networking before first boot, place NetworkManager connection files in the `CONFIG` partition (mounted at `/mnt/config` on the running system):

```text
CONFIG/
└── network/
    └── my-network
```

The NetworkManager configuration file at `/etc/NetworkManager/NetworkManager.conf` sets these defaults:

| Setting | Value | Purpose |
|---|---|---|
| `wifi.scan-rand-mac-address` | `no` | Disables random MAC addresses for consistent network identity |
| `connection.llmnr` | `2` (disabled) | Disables Link-Local Multicast Name Resolution |
| `connection.mdns` | `2` (enabled) | Enables mDNS for `.local` hostname resolution |
| `connectivity.uri` | `https://checkonline.home-assistant.io/online.txt` | Online connectivity check endpoint |

> [!NOTE]
> DNS resolution uses systemd-resolved with a fallback stub resolver configured at `/etc/resolv.conf`. If you experience DNS issues with local domain resolution, see the Troubleshooting section for known issue [#2988](https://github.com/home-assistant/operating-system/issues/2988).

### Swap Configuration

HAOS creates a swap file on the data partition at `/mnt/data/swapfile`. The swap size can be customized by writing to `/etc/default/haos-swapfile`:

```bash
echo 'SWAPSIZE=512M' > /etc/default/haos-swapfile
```

After setting, reboot for the change to take effect. The swap service (`haos-swapfile.service`) reads this file and creates a swap file of the specified size.

| Value | Behavior |
|---|---|
| `SWAPSIZE=512M` | Creates a 512 MB swap file |
| `SWAPSIZE=1G` | Creates a 1 GB swap file |
| `SWAPSIZE=0` | Disables swap entirely |
| *(not set)* | Uses the system default formula |

> [!WARNING]
> Setting `SWAPSIZE=0` completely disables swap. On memory-constrained devices (1 GB RAM or less), this may cause out-of-memory kills for Home Assistant or add-ons. Only disable swap if you have sufficient physical RAM.

### Hostname

The system hostname defaults to `homeassistant` and can be changed through the Home Assistant UI or CLI:

```bash
ha host options --hostname myhome
```

### Time Synchronization

Time synchronization uses `systemd-timesyncd`. The configuration at `/etc/systemd/timesyncd.conf` uses the default NTP servers. A NetworkManager dispatcher script at `/usr/lib/NetworkManager/dispatcher.d/10-ntp` manages NTP server updates when network connections change.

### Docker Configuration

Docker runs with the containerd snapshotter backend. The daemon configuration at `/etc/docker/daemon.json` is pre-configured by HAOS:

```json
{
    "log-driver": "journald",
    "storage-driver": "overlay2"
}
```

> [!IMPORTANT]
> Do not modify the Docker configuration manually. HAOS manages Docker settings through the Supervisor. Manual changes may be overwritten on updates or cause the Supervisor to fail.

For the complete configuration reference covering all system files and systemd units, see the **Admin Guide**.

---

## 4. Core Usage

### Checking OS Status

View the current OS version, board type, and boot slot:

```bash
ha os info
```

```text
version: 14.2
version_latest: 14.3
update_available: true
board: rpi4-64
boot: slot0
```

### Updating the OS

HAOS supports over-the-air updates. Updates are downloaded, installed to the inactive boot slot, and activated on the next reboot:

```bash
ha os update
```

```text
Processing... Done.
Command completed successfully.
```

The system reboots automatically into the updated slot. If the update fails to boot, RAUC automatically rolls back to the previous working slot.

> [!TIP]
> To update to a specific version rather than the latest:
> ```bash
> ha os update --version 14.3
> ```

### Managing Boot Slots

HAOS uses an A/B partition scheme with two kernel and two system slots. You can switch between them:

```bash
ha os boot-slot other
```

This reboots into the alternate slot. Use this to roll back to a previous OS version or test a different slot.

To check the current RAUC slot status at the system level:

```bash
rauc status
```

> [!NOTE]
> The `rauc status` command shows detailed information about both boot slots including version, boot status (good/bad), and which slot is currently active. The active slot is listed as `kernel.0` in the boot primary field.

### Viewing System Logs

Access system logs through the journal:

```bash
ha host logs
```

For Supervisor-specific logs:

```bash
ha su logs
```

For kernel messages:

```bash
dmesg
```

### Checking Service Status

View the status of all systemd services:

```bash
systemctl --no-pager -l status -a
```

Check for any failed services:

```bash
systemctl --no-pager -l list-units --state=failed
```

```text
0 loaded units listed.
```

> [!WARNING]
> If any services show as failed, note the service name and check its logs with `journalctl -u <service-name>`. Common failures after updates are documented in the Troubleshooting section.

### Checking Network Connectivity

Verify the system has internet connectivity:

```bash
nmcli network connectivity check
```

```text
full
```

You can also verify from within the Supervisor container:

```bash
curl -f https://checkonline.home-assistant.io/online.txt
```

```text
NetworkManager is online
```

---

## 5. Common Tasks

### Task: Create and Restore a Backup

**Goal:** Create a full system backup and restore it if needed.

**Steps:**

1. Create a backup:

```bash
ha backups new
```

```text
slug: abc123de
```

2. List available backups:

```bash
ha backups list
```

3. Restore a backup (including a specific add-on):

```bash
ha backups restore abc123de --app core_ssh
```

4. Restore a specific folder from a backup:

```bash
ha backups restore abc123de --folders ssl
```

**If it fails:** Check that the data partition has sufficient free space with `df -h /mnt/data`.

### Task: Move the Data Disk to External Storage

**Goal:** Move the HAOS data partition to an external USB drive or SSD for better performance and storage capacity.

**Steps:**

1. Connect the external drive to the device
2. Use the Home Assistant CLI to identify the drive:

```bash
ha os datadisk list
```

3. Move the data disk:

```bash
ha os datadisk move /dev/sda
```

> [!CAUTION]
> Moving the data disk erases all data on the target drive. This operation requires a reboot and cannot be undone easily. Create a backup before proceeding.

**Verify:** After reboot, confirm the new data disk:

```bash
ha os info
```

The `data_disk` field should show the new device path.

### Task: Configure a Static IP Address

**Goal:** Set a static IP instead of DHCP.

**Steps:**

1. Identify the network interface:

```bash
ha network info
```

2. Configure the static IP:

```bash
ha network update enp0s3 --ipv4-method static --ipv4-address 192.168.1.100/24 --ipv4-gateway 192.168.1.1 --ipv4-nameserver 8.8.8.8
```

**Verify:**

```bash
ha network info
```

### Task: Enable SSH Access

**Goal:** Access the HAOS command line remotely.

**Steps:**

1. Install the SSH add-on from the Home Assistant Add-on Store
2. Configure a password or authorized keys in the add-on configuration
3. Start the add-on
4. Connect via SSH:

```bash
ssh [email removed] -p 22222
```

> [!TIP]
> The `ha` CLI is available directly in the SSH session. For low-level OS access, use `login` at the console prompt to enter the host shell.

### Task: Check and Update Supervisor Components

**Goal:** Ensure all Supervisor components are up to date.

**Steps:**

1. Check component versions:

```bash
ha supervisor info
```

2. Update individual components:

```bash
ha supervisor update
ha dns update
ha audio update
ha cli update
ha observer update
ha multicast update
```

3. Verify no errors in the resolution center:

```bash
ha resolution info
```

**If it fails:** If an update reports "Another job is running," wait for the current operation to complete and retry.

### Task: Reset the System (Factory Wipe)

**Goal:** Reset HAOS to a clean state, removing all user data.

> [!CAUTION]
> This permanently deletes all Home Assistant configuration, add-ons, backups, and user data. This cannot be undone.

**Steps:**

1. From the host console or SSH:

```bash
ha os wipe-data
```

2. The system reboots and reinitializes with a fresh data partition.

---

## 6. Troubleshooting

### Diagnostic Decision Tree

```text
System won't boot?
├── LED activity? → Wait 5–20 min for first boot
├── No LED → Check power supply, re-flash image
└── Boots but no web UI → Check network, try IP directly

Web UI unreachable?
├── Can you ping the device? → Check port 8123
├── Can't ping → Check network cable/Wi-Fi, check router DHCP
└── Ping works, port closed → Check container status via console

OS update failed?
├── System rebooted to old version → RAUC rolled back, check logs
├── "Don't have an URL for OTA updates" → Reload Supervisor: ha su reload
└── Stuck in update → Wait, then check ha os info
```

### Error: Bluetooth not working in VirtualBox/Proxmox VM

**Cause:** Virtual machines do not have native Bluetooth hardware. USB passthrough of Bluetooth adapters may not work correctly with all hypervisors.

**Solution:**
1. Use USB passthrough for the physical Bluetooth adapter instead of virtual Bluetooth
2. In VirtualBox, enable USB 2.0 or 3.0 controller and add a USB device filter for the adapter
3. In Proxmox, pass through the USB device at the host level

**Prevention:** For Zigbee/Z-Wave, consider using a networked coordinator (e.g., USB adapter on another machine with ser2net) instead of direct USB passthrough.

*Related: [#2142](https://github.com/home-assistant/operating-system/issues/2142)*

### Error: DNS resolution for LAN hostnames broken

**Cause:** HAOS uses systemd-resolved which may not properly resolve local network hostnames, especially with `.local` domains that conflict with mDNS.

**Solution:**
1. Verify DNS settings: `ha network info`
2. Set explicit DNS servers: `ha network update <interface> --ipv4-nameserver 192.168.1.1`
3. If using a custom domain, ensure your local DNS server is reachable from the HAOS device

**Prevention:** Use IP addresses instead of hostnames for critical integrations, or ensure your local DNS infrastructure properly resolves all names.

*Related: [#2988](https://github.com/home-assistant/operating-system/issues/2988)*

### Error: OS update fails (e.g., 16.2 to 16.3)

**Cause:** Certain version transitions may fail due to OTA URL resolution issues or download failures during the update process.

**Solution:**
1. Reload the Supervisor to refresh version info:
   ```bash
   ha su reload
   ```
2. Retry the update:
   ```bash
   ha os update --version 16.3
   ```
3. If it persists, wait for ongoing downloads (Core, Supervisor) to complete before retrying

**Prevention:** Ensure stable internet connectivity during OS updates. Avoid updating during periods of high network activity.

*Related: [#4419](https://github.com/home-assistant/operating-system/issues/4419)*

### Error: OS stuck on old version — unable to update

**Cause:** The update mechanism may fail if the Supervisor cannot reach the version endpoint or if the OTA URL is not available for the current board.

**Solution:**
1. Check connectivity: `curl -f https://checkonline.home-assistant.io/online.txt`
2. Reload Supervisor data: `ha su reload`
3. Check for available updates: `ha os info`
4. If no update URL is available, manually download the `.raucb` update bundle and apply it locally

**Prevention:** Keep the Supervisor up to date, as older Supervisors may not correctly resolve OTA URLs for newer OS versions.

*Related: [#3633](https://github.com/home-assistant/operating-system/issues/3633)*

### Error: USB subsystem failure after OS update (RPi 4B)

**Cause:** Kernel updates may introduce regressions affecting USB controller drivers, particularly on Raspberry Pi 4B.

**Solution:**
1. Boot into the previous slot: `ha os boot-slot other`
2. Check kernel taint status: `cat /proc/sys/kernel/tainted` (should be `0`)
3. Report the issue with `dmesg` output if the kernel is tainted

**Prevention:** Before updating, check the release notes for known hardware-specific issues.

*Related: [#4627](https://github.com/home-assistant/operating-system/issues/4627)*

### Error: Samba shares fail on reboot

**Cause:** The Samba add-on may fail to start if its data directory mount is not ready when the service starts.

**Solution:**
1. Restart the Samba add-on from the Home Assistant UI
2. If persistent, check the data mount: `mount | grep /mnt/data`
3. Verify no failed systemd units: `systemctl list-units --state=failed`

*Related: [#4459](https://github.com/home-assistant/operating-system/issues/4459)*

### Error: FQDN resolution errors — `.local.hass.io` appended to hostnames

**Cause:** The system intermittently appends `.local.hass.io` to fully qualified domain names, causing DNS resolution failures.

**Solution:**
1. Check `/etc/resolv.conf` for unexpected search domains
2. Restart systemd-resolved: `systemctl restart systemd-resolved`
3. Verify DNS behavior: `resolvectl query example.com`

*Related: [#4434](https://github.com/home-assistant/operating-system/issues/4434)*

### Error: USB serial device (CP210x / Sonoff Zigbee) disappears periodically in VM

**Cause:** USB device enumeration issues in KVM/QEMU VMs, particularly with newer HAOS kernel versions.

**Solution:**
1. Verify USB passthrough configuration in the hypervisor
2. Check `dmesg` for USB disconnect/reconnect messages
3. Try a different USB controller type (e.g., switch from xHCI to EHCI)

**Prevention:** For VM-based installations, consider using a networked serial-to-TCP solution (ser2net) instead of USB passthrough.

*Related: [#4595](https://github.com/home-assistant/operating-system/issues/4595)*

> [!WARNING]
> After any OS update, always check `systemctl list-units --state=failed` to catch services that may not have started correctly. The RAUC boot slot rollback only triggers if the boot itself fails, not if individual services fail post-boot.

### Error: RPi 5 NVMe — boot slot B always marked bad

**Cause:** The Raspberry Pi 5 tryboot mechanism may incorrectly mark boot slot B as bad when using NVMe storage.

**Solution:**
1. Check RAUC status: `rauc status --detailed`
2. If slot B is marked bad, investigate via `rauc status mark-good`
3. Verify both slots show correct status

*Related: [#4632](https://github.com/home-assistant/operating-system/issues/4632)*

### Error: RPi 4B CPU stuck at max frequency after OS update

**Cause:** A kernel regression in the cpufreq driver may lock the CPU frequency at maximum, increasing power consumption and heat.

**Solution:**
1. Boot into the previous slot: `ha os boot-slot other`
2. Verify with `cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq`
3. Report with `dmesg` output attached

*Related: [#4633](https://github.com/home-assistant/operating-system/issues/4633)*

> [!CAUTION]
> Do not manually flash or modify the boot partition. HAOS uses RAUC for managed updates, and manual changes will break the A/B update mechanism and signature verification.

---

## 7. FAQ

### General

**Q: What is the difference between HAOS and a manual Home Assistant installation on regular Linux?**

HAOS is a dedicated, minimal operating system that manages the entire system stack — OS updates, Docker, networking, and the Home Assistant Supervisor. A manual installation gives you more control but requires you to manage all of these components yourself. HAOS is the recommended installation method for most users.

**Q: Can I install additional software on HAOS?**

HAOS is a read-only, managed operating system. You cannot install packages with `apt` or `yum`. Additional functionality should be added through Home Assistant add-ons, which run as Docker containers managed by the Supervisor.

**Q: How much disk space does HAOS need?**

A minimum of 32 GB is recommended. The OS itself uses approximately 2–4 GB for the system partitions. The data partition (for add-ons, backups, recordings, etc.) grows over time.

**Q: Does HAOS support Wi-Fi?**

Yes, on hardware with Wi-Fi capabilities (Raspberry Pi, certain USB adapters). Wi-Fi is managed through NetworkManager and can be configured via the Home Assistant UI or CLI.

### Updates

**Q: What happens if an OS update fails?**

HAOS uses an A/B partition scheme with RAUC. Updates are installed to the inactive slot. If the new version fails to boot, the system automatically rolls back to the previous working slot. Your data is never affected by OS updates.

**Q: Can I downgrade to a previous OS version?**

You can switch to the other boot slot which contains the previous version:

```bash
ha os boot-slot other
```

This reboots into the version that was active before the last update.

**Q: How do I disable automatic OS updates?**

Automatic OS updates are controlled by the Supervisor. In the updater configuration at `/mnt/data/supervisor/updater.json`, set:

```json
{"auto_update": false}
```

Then restart the Supervisor: `systemctl restart hassos-supervisor.service`

### Hardware

**Q: Can I use HAOS on a Raspberry Pi with an NVMe drive?**

Yes, the Raspberry Pi 5 (`rpi5-64` image) natively supports NVMe boot. For the Raspberry Pi 4, NVMe is supported via USB-to-NVMe adapters or PCIe HATs, but boot must start from the SD card with the data disk moved to NVMe.

**Q: My data disk is almost full. What can I do?**

1. Remove old backups: check Settings → System → Backups
2. Remove unused add-ons
3. Move the data disk to a larger drive: `ha os datadisk move /dev/sda`
4. Check Docker disk usage from the console: the `docker-disk-check` utility at `/usr/libexec/docker-disk-check` monitors available space

*Related: [#4607](https://github.com/home-assistant/operating-system/issues/4607)*

**Q: How do I check if my kernel is tainted?**

```bash
cat /proc/sys/kernel/tainted
```

A value of `0` means the kernel is clean. Any non-zero value indicates taint from out-of-tree modules, firmware issues, or other causes.

### Networking

**Q: Why does `homeassistant.local` not resolve?**

The `.local` domain uses mDNS (multicast DNS). If your router or network infrastructure blocks multicast traffic, mDNS will not work. Use the IP address directly, or configure your DNS server to resolve the hostname.

**Q: How do I fix IPv4 and IPv6 settings affecting each other?**

This is a known issue with NetworkManager configuration. When modifying network settings, configure IPv4 and IPv6 independently and avoid changing both protocols in the same `ha network update` command.

*Related: [#4637](https://github.com/home-assistant/operating-system/issues/4637)*

---

_Generated May 2026 from home-assistant/operating-system at a0b3eb6. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
