1. Architecture Overview
Home Assistant Operating System (HAOS) is a Buildroot-based Linux distribution purpose-built to host the Home Assistant ecosystem. It provides a minimal, hardened, and fully managed OS that boots directly into a Docker-based service stack running the Home Assistant Supervisor, Core, and add-ons.
Design Philosophy
HAOS is not a general-purpose Linux distribution. It is an appliance OS:
- The root filesystem is read-only (squashfs/erofs)
- User data lives on a separate data partition (
/mnt/data) - Configuration persistence uses bind mounts from the data partition to
/etcpaths - System updates are atomic A/B slot swaps via RAUC — no package manager
- All user-facing services (Home Assistant, add-ons) run as Docker containers
- The OS handles only hardware, networking, storage, and container orchestration
Architecture Diagram
┌─────────────────────────────────────────────────────────────┐
│ User Browser │
│ homeassistant.local:8123 │
└──────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────┴──────────────────────────────────┐
│ Docker (containerd) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐│
│ │ Supervisor │ │ Core │ │ Add-ons (DNS, ││
│ │ (hassio_ │ │ (home- │ │ Audio, CLI, ││
│ │ supervisor) │ │ assistant) │ │ Observer, etc.) ││
│ └──────┬───────┘ └──────────────┘ └──────────────────────┘│
│ │ manages all containers │
└─────────┼───────────────────────────────────────────────────┘
│
┌─────────┴───────────────────────────────────────────────────┐
│ HAOS (Buildroot Linux) │
│ ┌────────────┐ ┌──────────────┐ ┌────────────────────────┐│
│ │ systemd │ │ NetworkMgr │ │ RAUC (OTA updates) ││
│ │ services │ │ (networking) │ │ A/B slot management ││
│ └────────────┘ └──────────────┘ └────────────────────────┘│
│ ┌────────────┐ ┌──────────────┐ ┌────────────────────────┐│
│ │ AppArmor │ │ os-agent │ │ zRAM / swap ││
│ │ (security) │ │ (D-Bus API) │ │ (memory management) ││
│ └────────────┘ └──────────────┘ └────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
│
┌─────────┴───────────────────────────────────────────────────┐
│ Disk Partition Layout │
│ boot │ kernel.0 │ system.0 │ kernel.1 │ system.1 │ data │
│ (FAT)│ (A) │ (A) │ (B) │ (B) │ (ext4) │
└─────────────────────────────────────────────────────────────┘
Component Inventory
| Component | Path / Package | Role |
|---|---|---|
| Buildroot | buildroot/ (git submodule) |
Cross-compilation build system |
| Buildroot External | buildroot-external/ |
HAOS-specific packages, configs, overlays |
| Board Support | buildroot-external/board/ |
Per-hardware boot config, kernel configs, hooks |
| Package Definitions | buildroot-external/package/ |
Custom packages (os-agent, hassio, drivers, etc.) |
| Root FS Overlay | buildroot-external/rootfs-overlay/ |
Systemd units, config files, shell scripts |
| Kernel Configs | buildroot-external/kernel/ |
Kernel fragment configs per version |
| OTA/RAUC | buildroot-external/ota/ |
RAUC system config, signing certificates, hooks |
| Image Generation | buildroot-external/genimage/ |
Disk image layout configs |
| Boot Configuration | buildroot-external/bootloader/ |
U-Boot config, MBR images |
| Build Configs | buildroot-external/configs/ |
Buildroot defconfigs per board |
| Patches | buildroot-external/patches/ |
Patches for upstream packages (Docker, systemd, GRUB, etc.) |
| Build Scripts | buildroot-external/scripts/ |
Post-build, post-image, and utility scripts |
| Tests | tests/ |
Smoke tests and Supervisor integration tests (labgrid + pytest) |
| Container Scripts | scripts/ |
Development helpers (enter.sh, update scripts) |
Technology Stack
| Layer | Technology | Version / Notes |
|---|---|---|
| Build system | Buildroot | Git submodule, cross-compilation |
| Init system | systemd | PID 1, service management, journald |
| Container runtime | Docker + containerd | Containerd snapshotter backend |
| Networking | NetworkManager | Wi-Fi, Ethernet, VLAN, mDNS |
| DNS | systemd-resolved | Stub resolver, per-link DNS |
| OTA updates | RAUC | A/B slots, signed bundles, hooks |
| Bootloader | U-Boot (ARM) / GRUB (x86) | Per-board configuration |
| Security | AppArmor | Profiles for Supervisor containers |
| Filesystem | squashfs/erofs (system), ext4 (data) | Read-only system, read-write data |
| Memory | zRAM + swap file | Configurable sizes |
| D-Bus agent | os-agent | Host API for Supervisor communication |
| USB disk management | udisks2 | Data disk operations |
Note
HAOS uses Buildroot (not Yocto/OpenEmbedded). Buildroot produces a complete cross-compiled root filesystem image from source. The
buildroot/directory is a git submodule pinned to a specific Buildroot release, andbuildroot-external/contains all HAOS customizations using Buildroot's external tree mechanism (BR2_EXTERNAL).
2. System Requirements
Build Requirements
Building HAOS requires a Linux host (or Docker on any platform):
| Requirement | Minimum |
|---|---|
| OS | Linux (Ubuntu 22.04+ recommended) or Docker |
| Disk | 50+ GB free space |
| RAM | 4+ GB |
| Tools | make, gcc, git, docker (for containerized builds) |
The repository includes a Dockerfile for reproducible builds and a Makefile with board-specific targets.
Test Requirements
The test suite (tests/) requires:
| Package | Version | Purpose |
|---|---|---|
labgrid |
25.0.1 | Hardware/VM test automation framework |
pytest-dependency |
0.6.1 | Test ordering and dependency chains |
pytest-timeout |
2.4.0 | Per-test timeout enforcement |
| Python | 3.x (.python-version in tests/) |
Test runner |
Target Hardware Requirements
| Board Category | RAM | Storage | Architecture |
|---|---|---|---|
| Raspberry Pi 3/4/5 | 1–8 GB | 32+ GB SD/eMMC/NVMe | aarch64 |
| ODROID (C2/C4/M1/M1S/N2) | 2–4 GB | 32+ GB SD/eMMC | aarch64 |
| Khadas VIM3 | 2–4 GB | 32+ GB SD/eMMC | aarch64 |
| NabuCasa Green | 4 GB | 32 GB eMMC | aarch64 |
| Generic x86-64 | 2+ GB | 32+ GB SSD/NVMe | x86_64 |
| OVA (VM) | 2+ GB allocated | 32+ GB virtual disk | x86_64 |
3. Configuration Reference
Partition Layout
HAOS uses a fixed partition scheme created by genimage during the build:
| Partition | Label | Filesystem | Mount Point | Purpose |
|---|---|---|---|---|
| Boot | hassos-boot |
FAT32 | /mnt/boot |
Bootloader, kernel, DTBs, config.txt |
| Kernel A | hassos-kernel0 |
raw | — | Kernel image (slot A) |
| System A | hassos-system0 |
squashfs/erofs | / (when active) |
Root filesystem (slot A) |
| Kernel B | hassos-kernel1 |
raw | — | Kernel image (slot B) |
| System B | hassos-system1 |
squashfs/erofs | / (when active) |
Root filesystem (slot B) |
| Bootstate | — | raw | — | RAUC boot status tracking |
| Overlay | hassos-overlay |
ext4 | /mnt/overlay |
Persistent overlay for /etc |
| Data | hassos-data |
ext4 | /mnt/data |
User data, Docker, add-ons, backups |
Important
The partition table type varies by board: GPT for UEFI systems (
hdimage-gpt.cfg), hybrid MBR/GPT for some ARM boards (hdimage-hybrid.cfg), and pure MBR for legacy boards (hdimage-mbr.cfg). The genimage configs inbuildroot-external/genimage/define the exact layout for each type.
Disk Image Generation Configs
| Config File | Purpose |
|---|---|
genimage/genimage.cfg |
Top-level image assembly |
genimage/hdimage-gpt.cfg |
GPT partition table layout |
genimage/hdimage-hybrid.cfg |
Hybrid MBR/GPT layout |
genimage/hdimage-mbr.cfg |
MBR partition table layout |
genimage/images-boot.cfg |
Boot partition image |
genimage/images-os.cfg |
OS (system) partition image |
genimage/image-raucb-spl.cfg |
RAUC bundle with SPL |
genimage/image-raucb-nospl.cfg |
RAUC bundle without SPL |
Systemd Service Units
HAOS defines a comprehensive set of systemd services in buildroot-external/rootfs-overlay/usr/lib/systemd/system/:
Core OS Services
| Unit | Type | Purpose |
|---|---|---|
hassos-config.service |
oneshot | Reads and applies user configuration from /mnt/config |
hassos-data.service |
oneshot | Initializes data partition structure |
hassos-expand.service |
oneshot | Expands data partition to fill disk on first boot |
hassos-overlay.service |
oneshot | Sets up persistent overlay for /etc |
hassos-persists.service |
oneshot | Persists certain runtime state across reboots |
hassos-supervisor.service |
service | Starts the Home Assistant Supervisor container |
hassos-apparmor.service |
oneshot | Loads AppArmor profiles |
hassos-bind.target |
target | Ordering target for bind mounts |
hassos-hardware.target |
target | Ordering target for hardware readiness |
hassos-zram.target |
target | Ordering target for zRAM setup |
hassos-user-rules-udev-trigger.service |
oneshot | Re-triggers udev for user-supplied rules |
Data and Storage Services
| Unit | Type | Purpose |
|---|---|---|
mnt-boot.mount |
mount | Mounts boot partition to /mnt/boot |
mnt-config.mount |
mount | Mounts config partition to /mnt/config |
mnt-data.mount |
mount | Mounts data partition to /mnt/data |
mnt-overlay.mount |
mount | Mounts overlay partition to /mnt/overlay |
var.mount |
mount | Mounts /var from data partition |
var-lib-docker.mount |
mount | Bind-mounts Docker data directory |
var-lib-bluetooth.mount |
mount | Bind-mounts Bluetooth state |
var-lib-NetworkManager.mount |
mount | Bind-mounts NetworkManager state |
var-lib-systemd.mount |
mount | Bind-mounts systemd persistent state |
var-log-journal.mount |
mount | Bind-mounts journal log directory |
root-.docker.mount |
mount | Bind-mounts Docker config for root |
root-.ssh.mount |
mount | Bind-mounts SSH config for root |
haos-data-disk-detach.service |
oneshot | Safely detaches external data disk |
haos-swapfile.service |
oneshot | Creates and activates swap file |
mnt-data-swapfile.swap |
swap | Swap file unit |
Persistent Bind Mounts
| Unit | Purpose |
|---|---|
etc-default.mount |
Persists /etc/default from data partition |
etc-dropbear.mount |
Persists Dropbear SSH host keys |
etc-hostname.mount |
Persists hostname |
etc-hosts.mount |
Persists hosts file |
etc-modprobe.d.mount |
Persists modprobe configuration |
etc-sysctl.d.mount |
Persists sysctl settings |
etc-systemd-timesyncd.conf.mount |
Persists time sync configuration |
etc-udev-rules.d.mount |
Persists user udev rules |
etc-usb_modeswitch.d.mount |
Persists USB modeswitch config |
Memory Management Services
| Unit | Type | Purpose |
|---|---|---|
zram-tmp.service |
oneshot | Creates zRAM-backed /tmp |
zram-var.service |
oneshot | Creates zRAM-backed /var |
haos-mglru.service |
oneshot | Enables Multi-Generation LRU for improved memory reclaim |
haos-swapfile.service |
oneshot | Manages swap file creation and activation |
Other Services
| Unit | Type | Purpose |
|---|---|---|
docker-prepare.service |
oneshot | Pre-start Docker preparation |
ha-cli@.service |
template | Home Assistant CLI service template |
haos-bt-cache.service |
oneshot | Bluetooth device cache management |
haos-bt-cache.timer |
timer | Timer for Bluetooth cache service |
haos-wipe.service |
oneshot | Factory reset / data wipe |
raucdb-update.service |
oneshot | Updates RAUC database |
systemd-journal-gatewayd.socket |
socket | Journal gateway for Supervisor log access |
Note
Many upstream systemd services have drop-in overrides in subdirectories (e.g.,
docker.service.d/,NetworkManager.service.d/). These customize behavior without modifying the original unit files. For example,docker.service.d/hassos.confsets Docker dependencies and resource limits, anddocker.service.d/failure.confconfigures restart behavior on failure.
Systemd Presets
The file buildroot-external/rootfs-overlay/usr/lib/systemd/system-preset/70-haos.preset controls which services are enabled by default at boot.
System Configuration Files
| File | Format | Purpose |
|---|---|---|
/etc/fstab |
fstab | Static filesystem mount table |
/etc/resolv.conf |
resolv | DNS resolver (stub to systemd-resolved) |
/etc/motd |
text | Message of the day for console login |
/etc/input-event-daemon.conf |
ini | Input event daemon configuration |
/etc/NetworkManager/NetworkManager.conf |
ini | NetworkManager main configuration |
/etc/bluetooth/main.conf |
ini | Bluetooth daemon configuration |
/etc/containerd/config.toml |
TOML | Containerd runtime configuration |
/etc/docker/daemon.json |
JSON | Docker daemon configuration |
/etc/systemd/coredump.conf |
ini | Core dump handling configuration |
/etc/systemd/journald.conf |
ini | Journal logging configuration |
/etc/systemd/logind.conf |
ini | Login manager configuration |
/etc/systemd/resolved.conf |
ini | DNS resolution configuration |
/etc/systemd/timesyncd.conf |
ini | Time synchronization configuration |
Sysctl Settings
| File | Key Settings |
|---|---|
/usr/lib/sysctl.d/10-printk.conf |
Kernel print level tuning |
/usr/lib/sysctl.d/15-swappiness.conf |
Swap aggressiveness tuning |
/usr/lib/sysctl.d/20-network.conf |
Network buffer and connection tuning |
/usr/lib/sysctl.d/30-cp15barrier.conf |
ARM CP15 barrier workaround |
/usr/lib/sysctl.d/60-otbr-ancillary-buffer.conf |
OpenThread Border Router buffer tuning |
System Configuration Overrides
| File | Purpose |
|---|---|
/usr/lib/systemd/system.conf.d/etc-environment-overrides.conf |
Environment variable overrides for all services |
/usr/lib/systemd/system.conf.d/watchdog.conf |
Hardware watchdog configuration |
Tmpfiles Configuration
| File | Purpose |
|---|---|
/etc/tmpfiles.d/data.conf |
Data partition directory structure |
/etc/tmpfiles.d/nm.conf |
NetworkManager runtime directories |
/etc/tmpfiles.d/overlay.conf |
Overlay partition structure |
Tip
To understand the full boot sequence, trace the systemd dependency chain starting from
hassos-data.service→hassos-expand.service→hassos-overlay.service→hassos-config.service→hassos-supervisor.service. The bind mount units depend onhassos-bind.target, ensuring data is available before services that need persistent state.
4. Deployment & Scaling
Building HAOS Images
Containerized Build (Recommended)
The repository includes a Dockerfile and Makefile for reproducible builds:
make <board_target>
Where <board_target> is one of the defconfig names in buildroot-external/configs/:
| Target | Board |
|---|---|
rpi3_64 |
Raspberry Pi 3 (64-bit) |
rpi4_64 |
Raspberry Pi 4 (64-bit) |
rpi5_64 |
Raspberry Pi 5 (64-bit) |
yellow |
Home Assistant Yellow |
green |
NabuCasa Green |
odroid_c2 |
ODROID-C2 |
odroid_c4 |
ODROID-C4 |
odroid_m1 |
ODROID-M1 |
odroid_m1s |
ODROID-M1S |
odroid_n2 |
ODROID-N2/N2+ |
khadas_vim3 |
Khadas VIM3 |
generic_x86_64 |
Generic x86-64 (UEFI) |
generic_aarch64 |
Generic AArch64 (UEFI) |
ova |
OVA (Virtual Machine) |
Build Process Flow
make <target>
→ Docker container with Buildroot toolchain
→ BR2_EXTERNAL=buildroot-external
→ buildroot-external/configs/<target>_defconfig
→ Cross-compile kernel, packages, root filesystem
→ buildroot-external/scripts/post-build.sh
→ Root filesystem overlay applied
→ buildroot-external/scripts/post-image.sh
→ genimage creates disk image
→ RAUC bundle created (.raucb)
→ hassos-hook.sh (board-specific post-processing)
→ Compressed image (.img.xz) output
Development Shell
Enter the Buildroot build environment interactively:
scripts/enter.sh
This launches a Docker container with the build environment mounted, allowing direct access to Buildroot's make menuconfig, make linux-menuconfig, and other build system commands.
Board Support Architecture
Each supported board has a directory under buildroot-external/board/ containing:
| File | Purpose |
|---|---|
meta |
Board metadata (name, architecture, image type) |
cmdline.txt |
Kernel command line parameters |
hassos-hook.sh |
Build hook for board-specific image processing |
kernel.config |
Board-specific kernel configuration fragment |
uboot.config |
U-Boot configuration (ARM boards) |
uboot-boot.ush / uboot-boot64.ush |
U-Boot boot scripts |
boot-env.txt |
U-Boot environment variables |
config.txt |
Raspberry Pi firmware configuration (RPi boards only) |
grub.cfg |
GRUB configuration (x86 boards only) |
Note
The
hassos-hook.shscript for each board implements two functions:hassos_pre_image()(copies bootloader, DTBs, and firmware to the boot partition) andhassos_post_image()(post-processing like xz compression). The Raspberry Pi hook additionally enables 64-bit mode inconfig.txtfor 64-bit board targets.
Raspberry Pi-Specific Configuration
The Raspberry Pi boards use a shared base configuration with per-variant overrides:
buildroot-external/board/raspberrypi/
├── config.txt ← Shared RPi firmware config
├── cmdline.txt ← Shared kernel command line
├── hassos-hook.sh ← Shared build hook
├── kernel.config ← Shared kernel config fragment
├── uboot-boot.ush ← U-Boot script (32-bit)
├── uboot-boot64.ush ← U-Boot script (64-bit)
├── uboot.config ← U-Boot config
├── rpi3-64/meta ← RPi 3 metadata
├── rpi4-64/meta ← RPi 4 metadata
├── rpi5-64/ ← RPi 5 (has own cmdline, config, hook)
│ ├── cmdline.txt
│ ├── config.txt
│ ├── hassos-hook.sh
│ └── rootfs-overlay/usr/lib/rauc/
│ ├── cmdline.sh ← RAUC cmdline update hook
│ └── rpi-tryboot.sh ← RPi 5 tryboot mechanism
└── yellow/ ← HA Yellow (CM4/CM5 carrier board)
├── config.txt
├── cmdline.txt
├── patches/ ← Yellow-specific kernel & U-Boot patches
└── rootfs-overlay/ ← Yellow firmware (brcmfmac)
Important
The Raspberry Pi 5 uses a different boot mechanism (
tryboot) instead of U-Boot for slot switching. Therpi-tryboot.shRAUC hook script manages this. This is whyrpi5-64has its ownhassos-hook.shseparate from the shared RPi hook.
5. Operations
OTA Update System (RAUC)
HAOS uses RAUC for over-the-air updates with an A/B partition scheme.
RAUC Configuration
The RAUC system configuration template is at buildroot-external/ota/system.conf.gtpl and defines:
- Slot definitions (kernel.0, kernel.1, system.0, system.1)
- Keyring for bundle signature verification
- Status file location for boot tracking
- Bundle format and compatibility string
Signing Infrastructure
| File | Purpose |
|---|---|
buildroot-external/ota/rel-ca.pem |
Release signing CA certificate |
buildroot-external/ota/dev-ca.pem |
Development signing CA certificate |
buildroot-external/ota/provisioning-ca.pem |
Provisioning CA certificate |
buildroot-external/ota/manifest.raucm.gtpl |
RAUC manifest template |
buildroot-external/ota/rauc-hook |
RAUC installation hook script |
buildroot-external/scripts/rauc.sh |
RAUC bundle creation script |
buildroot-external/scripts/generate-signing-key.sh |
Key generation utility |
Warning
RAUC bundles are cryptographically signed. The three CA certificates (release, development, provisioning) form a trust chain. Only bundles signed with a trusted key are accepted for installation. Modifying or removing these certificates will break the OTA update mechanism.
Update Flow
1. Supervisor checks version.home-assistant.io for updates
2. Downloads .raucb bundle for the board
3. RAUC verifies bundle signature against trusted CA chain
4. RAUC installs kernel + system to INACTIVE slot (A→B or B→A)
5. RAUC marks new slot as primary boot target
6. System reboots into new slot
7. If boot succeeds → new slot marked "good"
8. If boot fails → bootloader falls back to previous "good" slot
Service Management
Supervisor Lifecycle
The Supervisor is managed by hassos-supervisor.service, which:
- Depends on Docker being ready
- Runs
/usr/sbin/hassos-supervisorto start the Supervisor container - The Supervisor then manages all other containers (Core, add-ons, plugins)
The Supervisor container is tagged as ghcr.io/home-assistant/{arch}-hassio-supervisor:latest during the build process by buildroot-external/package/hassio/dind-import-containers.sh.
Key OS Scripts
| Script | Path | Purpose |
|---|---|---|
hassos-supervisor |
/usr/sbin/hassos-supervisor |
Starts/manages the Supervisor container |
hassos-config |
/usr/sbin/hassos-config |
Applies user configuration from /mnt/config |
hassos-cli |
/usr/sbin/hassos-cli |
Host-level CLI entry point |
hassos-apparmor |
/usr/libexec/hassos-apparmor |
Loads AppArmor profiles |
hassos-data |
/usr/libexec/hassos-data |
Initializes data partition structure |
hassos-expand |
/usr/libexec/hassos-expand |
Expands data partition to fill disk |
hassos-overlay |
/usr/libexec/hassos-overlay |
Sets up persistent overlay |
hassos-persists |
/usr/libexec/hassos-persists |
Persists runtime state |
hassos-zram |
/usr/libexec/hassos-zram |
Configures zRAM devices |
haos-swapfile |
/usr/libexec/haos-swapfile |
Manages swap file |
haos-wipe |
/usr/libexec/haos-wipe |
Factory reset handler |
haos-freeze-hook |
/usr/libexec/haos-freeze-hook |
Filesystem freeze hook for backups |
haos-data-disk-detach |
/usr/libexec/haos-data-disk-detach |
Safe external disk detach |
docker-prepare |
/usr/libexec/docker-prepare |
Pre-Docker startup preparation |
docker-disk-check |
/usr/libexec/docker-disk-check |
Docker disk space monitoring |
raucdb-update |
/usr/libexec/raucdb-update |
RAUC database maintenance |
Tip
The boot sequence flows:
hassos-data.service(initialize data) →hassos-expand.service(expand partition) →hassos-overlay.service(overlay setup) → bind mounts →hassos-config.service(apply config) →docker-prepare.service→docker.service→hassos-supervisor.service. Tracing this chain is essential for debugging boot failures.
Container Image Pre-loading
During the build, HAOS pre-loads Docker container images into the data partition to enable offline first boot. The process is managed by three scripts in buildroot-external/package/hassio/:
-
fetch-container-image.sh— Downloads container images usingskopeowith architecture-aware selection and retry logic (3 retries with exponential backoff). Uses file locking to prevent race conditions during parallel downloads. -
dind-import-containers.sh— Runs inside a Docker-in-Docker container during the build. Loads pre-fetched OCI archives into the Docker daemon and tags the Supervisor image with the expected naming convention. -
create-data-partition.sh— Creates and populates the data partition image:- Creates a 1280 MB ext4 image labeled
hassos-data - Mounts and populates with Docker images via DinD
- Sets up initial AppArmor profiles from
version.home-assistant.io - Writes the updater channel configuration (
updater.json) - Creates the
.docker-use-containerd-snapshottermarker file
- Creates a 1280 MB ext4 image labeled
Note
The data partition image created during build contains pre-loaded container images so the system can boot without internet. The initial size is 1280 MB but is expanded to fill the disk on first boot by
hassos-expand.service.
Monitoring and Health
The test suite validates these health indicators that administrators should also monitor:
- No failed systemd units:
systemctl list-units --state=failedshould report 0 failures - No systemd dependency cycles: Check
journalctl -b0 | grep 'ordering cycle' - Kernel not tainted:
cat /proc/sys/kernel/taintedshould be0 - Network connectivity:
nmcli network connectivity checkshould reportfull - No AppArmor denies:
journalctl -t audit | grep DENIEDshould return empty - Supervisor health:
ha resolution healthcheckshould pass with no unhealthy/unsupported entries - RAUC slot status:
rauc statusshould show active slot with correct version and no warnings
6. Performance
Memory Management
HAOS implements several memory optimization strategies:
zRAM
Two zRAM devices are configured by hassos-zram (/usr/libexec/hassos-zram):
| Device | Mount | Purpose |
|---|---|---|
zram-tmp |
/tmp |
Compressed in-memory temporary storage |
zram-var |
/var |
Compressed in-memory variable data |
zRAM provides transparent compression of temporary data, reducing disk I/O and extending the effective memory capacity on memory-constrained devices.
Multi-Generation LRU (MGLRU)
The haos-mglru.service enables the Multi-Generation LRU page reclaim algorithm, which improves memory management efficiency compared to the traditional LRU, particularly beneficial for systems running many containerized services.
Swap File
The swap file at /mnt/data/swapfile provides additional virtual memory. The size is managed by haos-swapfile.service reading /etc/default/haos-swapfile. Swap aggressiveness is tuned via /usr/lib/sysctl.d/15-swappiness.conf.
Docker Optimization
- Containerd snapshotter backend: Enabled when the
.docker-use-containerd-snapshottermarker exists on the data partition, improving container layer handling - journald logging: Docker uses
journaldas the log driver, eliminating separate log files and enabling unified log access throughjournalctl - overlay2 storage driver: Efficient copy-on-write filesystem for container layers
- Resource limits: Docker service drop-ins in
docker.service.d/resource.confconfigure resource constraints
7. Security
AppArmor
HAOS uses AppArmor to provide mandatory access control for containerized services. The hassos-apparmor.service loads profiles at boot via /usr/libexec/hassos-apparmor. AppArmor profiles for the Supervisor are downloaded from version.home-assistant.io during the build and stored at /mnt/data/supervisor/apparmor/.
Warning
AppArmor denies indicate a container attempted an operation outside its allowed profile. Check
journalctl -t audit | grep DENIEDto diagnose. Persistent denies after updates may indicate a profile mismatch — the Supervisor manages profile updates automatically.
Filesystem Protection
- Read-only root: The system partition is squashfs/erofs, preventing runtime modification
- Persistent overlay: Only
/etcchanges persist via the overlay partition - Bind mounts: Specific directories (
/var/lib/docker,/var/lib/bluetooth, etc.) are bind-mounted from the data partition, isolating persistent state - Separate data partition: User data, Docker containers, and add-ons live on a separate ext4 partition, preventing system corruption from data issues
OTA Update Security
- Signed bundles: All RAUC update bundles are cryptographically signed
- Certificate chain: Three-tier CA (release, development, provisioning) for different deployment stages
- Verification on install: RAUC validates the bundle signature before writing to disk
- Rollback protection: Failed boots automatically revert to the last known-good slot
Network Security
- mDNS only: The system is discoverable via mDNS (
.local) only, not via LLMNR - Dropbear SSH: Minimal SSH server with persistent host keys via bind mount, configured via drop-in overrides for security hardening
- NTP dispatcher: The NetworkManager dispatcher (
10-ntp) ensures time synchronization updates when network state changes, preventing time-skew-based attacks
Docker Security
- AppArmor profiles per container: Managed by the Supervisor
- Container isolation: Each add-on runs in its own container with defined capabilities
- Docker daemon patches: HAOS applies custom patches to Docker (in
buildroot-external/patches/docker-engine/) including:- Respecting explicit AppArmor profiles on privileged containers
- Protecting bridge subnets from direct external access
Caution
The Docker engine patches in
buildroot-external/patches/docker-engine/modify upstream Docker behavior. Patch0002-bridge-protect-bridge-subnet-from-direct-external-ac.patchadds firewall rules to prevent external access to the Docker bridge subnet. Removing this patch would expose container networks to the local network.
8. Developer Internals
Repository Structure
home-assistant/operating-system/
├── Dockerfile ← Build container definition
├── Makefile ← Board-specific build targets
├── README.md ← Project overview
├── .gitmodules ← Buildroot submodule reference
├── .hadolint.yaml ← Dockerfile linter config (ignores DL3008)
├── buildroot/ ← Buildroot (git submodule)
├── buildroot-external/ ← HAOS customization layer
│ ├── Config.in ← Kconfig menu for HAOS packages
│ ├── external.desc ← Buildroot external descriptor
│ ├── external.mk ← Buildroot external makefile
│ ├── busybox.config ← BusyBox configuration
│ ├── meta ← Project metadata
│ ├── board/ ← Per-board configurations
│ │ ├── arm-uefi/ ← Generic ARM UEFI boards
│ │ ├── hardkernel/ ← ODROID boards
│ │ ├── khadas/ ← Khadas VIM boards
│ │ ├── nabucasa/ ← NabuCasa Green
│ │ ├── pc/ ← x86-64 and OVA
│ │ └── raspberrypi/ ← RPi 3/4/5/Yellow
│ ├── bootloader/ ← MBR images, U-Boot config
│ ├── configs/ ← Buildroot defconfigs
│ ├── genimage/ ← Disk image layout configs
│ ├── kernel/ ← Kernel config fragments
│ ├── ota/ ← RAUC config, CA certs, hooks
│ ├── package/ ← Custom packages
│ │ ├── bluetooth-rtl8723/ ← RTL8723 Bluetooth driver
│ │ ├── eq3_char_loop/ ← eQ-3 HomeMatic kernel module
│ │ ├── gasket/ ← Google Coral TPU driver
│ │ ├── generic_raw_uart/ ← Generic raw UART driver
│ │ ├── hailo-pci/ ← Hailo AI accelerator PCI driver
│ │ ├── hailo8-firmware/ ← Hailo-8 firmware blobs
│ │ ├── hardkernel-boot/ ← Hardkernel bootloader
│ │ ├── hassio/ ← Supervisor container pre-loading
│ │ ├── khadas-boot/ ← Khadas bootloader
│ │ ├── lxd-guest-agent/ ← LXD VM guest agent
│ │ ├── os-agent/ ← Home Assistant OS Agent
│ │ ├── pi-bluetooth/ ← Raspberry Pi Bluetooth
│ │ ├── qemu-guest-agent/ ← QEMU VM guest agent
│ │ ├── rockchip-blobs/ ← Rockchip binary firmware
│ │ ├── rpi-eeprom/ ← RPi EEPROM update tools
│ │ ├── rpi-rf-mod/ ← RPi RF module (HomeMatic)
│ │ ├── rtl88x2bu/ ← Realtek Wi-Fi driver
│ │ ├── tempio/ ← Template I/O (host tool)
│ │ ├── udisks2/ ← UDisks2 (patched for HAOS)
│ │ ├── vcgencmd/ ← VideoCore GPU command tool
│ │ └── xe-guest-utilities/ ← Xen VM guest utilities
│ ├── patches/ ← Patches for upstream packages
│ │ ├── docker-engine/ ← Docker security patches
│ │ ├── grub2/ ← GRUB enhancements
│ │ ├── libglib2/ ← GLib journal logging fix
│ │ ├── linux/ ← Kernel patches (IPv6 reachability)
│ │ ├── network-manager/ ← NM connectivity check fix
│ │ ├── rauc/ ← RAUC journal logging
│ │ ├── systemd/ ← systemd patches (hostname, journal, time)
│ │ ├── uboot/ ← U-Boot string-to-env command
│ │ └── uboot-tools/ ← fw_printenv lock file path
│ ├── rootfs-overlay/ ← Files overlaid onto root filesystem
│ │ ├── etc/ ← Configuration files
│ │ ├── mnt/ ← Mount point placeholders
│ │ └── usr/ ← Services, scripts, rules
│ └── scripts/ ← Build and utility scripts
├── Documentation/ ← Kernel version docs
├── scripts/ ← Developer helper scripts
└── tests/ ← Smoke and Supervisor tests
Package System
HAOS custom packages follow the Buildroot package convention:
| File | Purpose |
|---|---|
Config.in |
Kconfig menu entry (dependencies, help text) |
<package>.mk |
Buildroot makefile (download, build, install rules) |
<package>.hash |
SHA256 hash for source verification |
Patches (0001-*.patch) |
Applied to upstream source during build |
Each package's .mk file defines:
*_VERSION— upstream version to download*_SITE— download URL or GitHub repository*_DEPENDENCIES— build-time dependencies*_INSTALL_TARGET_CMDS— installation commands
Notable Custom Packages
os-agent (buildroot-external/package/os-agent/): The Home Assistant OS Agent provides a D-Bus interface for the Supervisor to communicate with the host OS. It handles OS operations like network management, disk operations, and system information queries.
hassio (buildroot-external/package/hassio/): Not a single binary but a set of build-time scripts that pre-load the Supervisor and plugin container images into the data partition. Contains create-data-partition.sh, dind-import-containers.sh, and fetch-container-image.sh.
udisks2 (buildroot-external/package/udisks2/): UDisks2 patched for HAOS with three patches:
- Allow explicitly disabling libsystemd-login
- Isolate authorization-specific functions
- Make polkit dependency optional
These patches enable UDisks2 to run without a full login/session manager or polkit daemon.
Guest Agents: Three guest agent packages support different virtualization platforms:
qemu-guest-agent— for KVM/QEMU VMslxd-guest-agent— for LXC/LXD containersxe-guest-utilities— for Xen/XCP-ng VMs
Upstream Patches
HAOS patches several upstream packages to customize behavior:
| Package | Patches | Purpose |
|---|---|---|
| Docker Engine | 2 | AppArmor profile respect on privileged containers; bridge subnet protection |
| systemd | 8 | Hostname on read-only /etc, journal gateway boots endpoint, time sync delay, environment variable paths |
| GRUB2 | 2 | file_env command for loading vars from files; legacy loader for broken Intel NUCs |
| Linux kernel | 1 | IPv6 reachability test explicit enable option |
| NetworkManager | 1 | Restrict connectivity check to per-link DNS lookups |
| RAUC | 1 | Add SYSLOG_IDENTIFIER to structured logs |
| U-Boot | 1 | String-to-environment-variable read command |
| U-Boot tools | 1 | Use /run as default lock file location for fw_printenv |
| libglib2 | 1 | Add SYSLOG_IDENTIFIER to structured logs |
Kernel Configuration
Kernel configuration uses a layered fragment approach:
- Board-specific config (
buildroot-external/board/<board>/kernel.config) — architecture and hardware-specific settings - Version fragments (
buildroot-external/kernel/v6.12.y/) — per-kernel-version features:
| Fragment | Purpose |
|---|---|
hassos.config |
Core HAOS kernel features |
docker.config |
Docker/container support (cgroups, namespaces, overlay) |
device-support.config |
Hardware device drivers |
device-support-pci.config |
PCI device support |
device-support-wireless.config |
Wireless/Bluetooth drivers |
device-support-wireless-pci.config |
PCIe wireless adapters |
Additional board-specific kernel patches are applied from buildroot-external/board/<board>/patches/linux/.
Note
The script
buildroot-external/scripts/check-dotconfig.py(using the vendoredkconfiglib.py) validates that all kernel config fragments are correctly applied to the final.config. This prevents silent config regressions.
Build Scripts
| Script | Purpose |
|---|---|
buildroot-external/scripts/post-build.sh |
Runs after root filesystem build — applies overlays, generates configs |
buildroot-external/scripts/post-image.sh |
Runs after image creation — calls genimage, creates RAUC bundles |
buildroot-external/scripts/rootfs-layer.sh |
Root filesystem layering helper |
buildroot-external/scripts/hdd-image.sh |
HDD/SSD image creation for x86 |
buildroot-external/scripts/rauc.sh |
RAUC bundle creation and signing |
buildroot-external/scripts/name.sh |
Version/name generation for build artifacts |
scripts/enter.sh |
Enter the Docker build environment |
scripts/entry.sh |
Docker container entry point |
scripts/update-dtb.sh |
Update device tree blobs |
scripts/update-firmware-rpi.sh |
Update RPi firmware files |
scripts/update-kernel-rpi.sh |
Update RPi kernel |
scripts/update-kernel-upstream.sh |
Update upstream kernel |
scripts/check-kernel-patches.sh |
Verify kernel patches apply cleanly |
Testing Strategy
HAOS uses labgrid for hardware-in-the-loop testing, combined with pytest.
Test Architecture
pytest
└── labgrid (test automation framework)
├── ShellDriver (serial/SSH console interaction)
├── qemu-strategy.yaml (QEMU VM target definition)
└── qemu_shell_strategy.py (QEMU-specific strategy)
Tests interact with a running HAOS instance (real hardware or QEMU VM) via shell commands, using shell.run_check() for command execution and shell_json() for JSON-parsed output.
Test Suites
Smoke Tests (tests/smoke_test/):
| Test | What It Validates |
|---|---|
test_init |
System boots, containers running, ha os info responds |
test_rauc_status |
RAUC reports kernel.0 as primary, version matches, no warnings |
test_dmesg |
Kernel messages captured (diagnostic) |
test_supervisor_logs |
Supervisor logs accessible |
test_landing_page |
HTTP GET to localhost:8123 returns HTML |
test_systemctl_status |
All systemd units status captured |
test_systemctl_check_no_failed |
Zero failed systemd units |
test_systemctl_no_cycles |
No systemd dependency ordering cycles |
test_host_connectivity |
Host can reach checkonline.home-assistant.io, nmcli reports full connectivity |
test_supervisor_connectivity |
Supervisor container can reach internet via docker0 bridge |
test_hassio_connectivity |
CLI container can reach internet via hassio bridge |
test_custom_swap_size |
Custom SWAPSIZE in /etc/default/haos-swapfile correctly applied after reboot |
test_no_swap |
SWAPSIZE=0 correctly disables swap with no service failures |
test_kernel_not_tainted |
/proc/sys/kernel/tainted is 0 |
Offline Test (tests/smoke_test/test_offline.py):
| Test | What It Validates |
|---|---|
test_ha_runs_offline |
Home Assistant starts and serves the landing page without internet access |
OS Update Tests (tests/smoke_test/test_os_update.py):
| Test | What It Validates |
|---|---|
test_init |
System boots and Supervisor reaches latest version |
test_os_update |
OS updates to latest stable via ha os update, system reboots to new version |
test_boot_other_slot |
Boot slot switch works, system runs on previous version |
Supervisor Tests (tests/supervisor_test/):
| Test | What It Validates |
|---|---|
test_start_supervisor |
Supervisor starts, responds to ping, Core installs |
test_check_supervisor |
Supervisor and network info APIs respond correctly |
test_update_components |
All components (supervisor, audio, cli, dns, observer, multicast) can update |
test_supervisor_is_updated |
Supervisor version matches latest |
test_app_install |
Core SSH add-on installs and starts |
test_supervisor_errors |
Resolution center health check passes, no unhealthy/unsupported entries |
test_create_backup |
Backup creation succeeds |
test_app_uninstall |
Core SSH add-on uninstalls cleanly |
test_restart_supervisor |
Supervisor restarts and returns to healthy state |
test_restore_backup |
Backup restore recovers add-on state |
test_restore_ssl_directory |
Backup restore recovers specific folder (ssl) |
test_no_apparmor_denies |
No AppArmor DENIED entries in audit log |
test_kernel_not_tainted |
Kernel remains untainted after full test suite |
Running Tests
cd tests
pip install -r requirements.txt
pytest --lg-env qemu-strategy.yaml smoke_test/
pytest --lg-env qemu-strategy.yaml supervisor_test/
Tip
The
test_inittest disables auto-updates before running the suite by settingauto_update: falsein/mnt/data/supervisor/updater.jsonand restartinghassos-supervisor.service. This prevents background updates from interfering with test assertions.
Udev Rules
| Rule File | Purpose |
|---|---|
/usr/lib/udev/rules.d/80-hotplug-cpu.rules |
Hot-plug CPU management |
/usr/lib/udev/rules.d/99-com.rules |
RPi community device rules |
rpi-rf-mod/82-rpi-rf-mod-leds.rules |
RPi RF module LED control |
mDNS / DNS-SD
The file /usr/lib/systemd/dnssd/workstation.dnssd registers the system as a workstation service via DNS-SD, enabling automatic discovery on the local network.
Known Issues and Community Feedback
Based on GitHub issues tracked for this project:
| Issue | Status | Impact |
|---|---|---|
| #2142 | Open | Bluetooth in VirtualBox/Proxmox VMs |
| #2988 | Open | LAN DNS resolution broken |
| #4642 | Open | Improve systemd shutdown ordering |
| #4481 | Open | Smarter default swap size formula |
| #4121 | Open | Python logging to systemd-journald |
| #4631 | Open | RPi EEPROM update feature |
| #4604 | Open | Better support for OS forks |
| #4223 | Open | CH341 serial driver not loading |
Contributing
Development documentation is hosted at developers.home-assistant.io/docs/operating-system/. The documentation source is in a separate repository: home-assistant/developers.home-assistant.
The kernel versions used in the current branch are documented in Documentation/kernel.md.
9. Integration Points
Home Assistant Supervisor
The Supervisor communicates with the OS through:
- os-agent — D-Bus interface for host operations (network, disk, system info)
- Docker socket — Container lifecycle management
- journald — Centralized logging via
systemd-journal-gatewayd /mnt/data— Shared data volume for add-on storage, backups, configuration
Virtualization Guest Agents
HAOS includes guest agent packages for major virtualization platforms:
| Platform | Agent | Service Unit |
|---|---|---|
| KVM/QEMU | qemu-guest-agent |
qemu-guest.service |
| LXC/LXD | lxd-guest-agent |
lxd-agent.service |
| Xen/XCP-ng | xe-guest-utilities |
xe-daemon.service |
These agents provide host-guest communication for operations like graceful shutdown, filesystem freeze, and guest information reporting.
Hardware Acceleration
HAOS includes drivers for hardware accelerators:
| Device | Package | Purpose |
|---|---|---|
| Google Coral TPU | gasket |
Coral Edge TPU PCI driver for ML inference |
| Hailo-8 | hailo-pci + hailo8-firmware |
Hailo AI accelerator for ML inference |
Home Automation Hardware
| Device | Package | Purpose |
|---|---|---|
| eQ-3 HomeMatic | eq3_char_loop |
Kernel module for HomeMatic CCU communication |
| RPI-RF-MOD | rpi-rf-mod |
Raspberry Pi radio frequency module (HomeMatic) |
| Generic Raw UART | generic_raw_uart |
Raw UART access for radio modules |
| Raspberry Pi Bluetooth | pi-bluetooth |
RPi-specific Bluetooth initialization |
| RTL8723 Bluetooth | bluetooth-rtl8723 |
Realtek Bluetooth driver |
| RTL88x2BU Wi-Fi | rtl88x2bu |
Realtek USB Wi-Fi driver |