Zero-config CLI for running Home Assistant OS on Apple Silicon using the native Virtualization framework. One command from download to boot.
Downloads and prepares HA OS automatically on first run. No Shortcuts, AppleScript, or manual launchd plists.
Designed for headless operation as a launchd service. Fire-and-forget: your smart home boots with your Mac.
All HA OS data — configs, add-ons, history — lives on a raw disk image. NVRAM and MAC address survive reboots.
Attach coordinators via the menu bar item. Hot-plug, no restart needed. macOS 27+.
Virtual CONFIG disk imports your public key for root SSH on port 22222 — HA OS picks it up automatically.
REST API → SSH → force-stop fallback on SIGTERM. Three methods before pulling the plug.
Built-in HTTP endpoint for monitoring VM state and USB accessories. Grafana-ready.
No Homebrew at runtime, no Python, no shell scripts. A single ~2.1 MB binary with zero external dependencies.
Fetches the latest stable HA OS release from GitHub. Cached for instant re-installs.
Built-in XZ decoder via libzma — no external tools, no Homebrew dependencies.
Copies and resizes the raw disk image. APFS sparse files keep it lean.
UEFI boot from the GPT disk. No kernel extraction, no initrd. Just point at the image.
$ brew install ingmarstein/havm/havm ==> Fetching downloads for: havm ✔︎ Formula havm (0.2.2) Verified 857.5KB/857.5KB ==> Installing havm from ingmarstein/havm ==> Caveats Downloads and sets up Home Assistant OS automatically on first run. Data: /opt/homebrew/var/lib/havm/ Optional config: /opt/homebrew/etc/havm/config.yml To start ingmarstein/havm/havm now and restart at login: brew services start ingmarstein/havm/havm Or, if you don't want/need a background service you can just run: /opt/homebrew/opt/havm/bin/havm run -c /opt/homebrew/etc/havm/config.yml --data-dir /opt/homebrew/var/lib/havm ==> Summary 🍺 /opt/homebrew/Cellar/havm/0.2.2: 12 files, 2.2MB, built in 2 seconds ==> Running `brew cleanup havm`... ==> Caveats fish completions have been installed to: /opt/homebrew/share/fish/vendor_completions.d
Then run it — havm handles the rest automatically:
❯ havm run 2026-06-29T20:26:47+0200 info havm.run: [Havm] Config loaded: CPU=4 Memory=4 GiB Network=bridge Log=text 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] Starting HA OS setup... 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] Found HA OS 18.0: haos_generic-aarch64-18.0.img.xz 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] Using cached image: /Users/ingmar/Library/Caches/havm/haos_generic-aarch64-18.0.img 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] Copied disk image to /Users/ingmar/Library/Application Support/havm/vm/haos.img 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] Resizing disk from 963661824 B to 32 GiB... 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] Disk resized. HA OS will auto-expand partitions on first boot. 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] SSH key path configured: ~/.ssh/id_ed25519.pub 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] SSH CONFIG disk created at /Users/ingmar/Library/Application Support/havm/vm/config.img 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] ✅ HA OS setup complete. 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] CPU: 4, Memory: 4 GiB 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] SSH CONFIG disk attached (USB) 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] USB: 1 device(s) 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] Network: Bridge (en0, MAC ca:cb:0a:be:56:71) 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] VM configuration validated successfully 2026-06-29T20:26:47+0200 info havm.run: [HavmCore] Starting VM... 2026-06-29T20:26:48+0200 info havm.run: [HavmCore] VM started successfully 2026-06-29T20:26:48+0200 info havm.run: [HavmRuntime] VM state: running 2026-06-29T20:26:48+0200 info havm.run: [HavmRuntime] VM is running. Press Ctrl+C to stop, or send SIGTERM for graceful shutdown. ╔══════════════════════════════════════════════════════════╗ ║ Home Assistant OS is booting. ║ ║ ║ ║ Once ready, open: ║ ║ http://homeassistant.local:8123 ║ ║ ║ ║ Or check your router's DHCP lease table for the VM's ║ ║ IP address and open http://<ip>:8123 ║ ╚══════════════════════════════════════════════════════════╝ 2026-06-29T20:26:48+0200 info havm.run: [HavmRuntime] USB: Listener registered — 0 already connected 2026-06-29T20:26:49+0200 info havm.run: [HavmRuntime] Waiting for resolution of homeassistant.local... 2026-06-29T20:27:03+0200 info havm.run: [HavmRuntime] Guest reachable at 192.168.1.42 — Home Assistant should be ready shortly 2026-06-29T20:27:03+0200 info havm.run: [HavmRuntime] Web: http://homeassistant.local:8123 2026-06-29T20:27:03+0200 info havm.run: [HavmRuntime] SSH: ssh root@homeassistant.local -p 22222 2026-06-29T20:27:31+0200 info havm.run: [HavmRuntime] Home Assistant is ready at http://homeassistant.local:8123
| Component | Choice | Why |
|---|---|---|
| Boot | UEFI (VZEFIBootLoader) | Boots directly from the GPT disk image |
| CPU | 4 cores (configurable) | Sufficient for HA OS + add-ons |
| Memory | 4 GiB (configurable) | Balloon lets macOS reclaim idle guest memory |
| Entropy | VirtIO entropy device | Random numbers for guest crypto and ASLR |
| Disk | 32 GiB raw image, VirtIO block | APFS sparse on disk (~6 GiB used after first boot) |
| Network | Bridge with stable MAC | LAN-reachable IP; falls back to NAT without entitlement |
| CONFIG disk | USB mass storage (XHCI) | HA OS imports SSH keys from USB, not VirtIO |
| NVRAM | Persisted EFI variable store | GRUB boot state survives reboots |
| Platform | VZGenericPlatformConfiguration | Stable machine ID → consistent MAC |
Or run as a background service that starts at login:
| Foreground | Service | |
|---|---|---|
| VM data | ~/Library/Application Support/havm/vm/ | /opt/homebrew/var/lib/havm/ |
| Config (optional) | ~/.config/havm/config.yml | /opt/homebrew/etc/havm/config.yml |
| Downloads cache | ~/Library/Caches/havm/ | |
Everything works with zero config. Tweak ~/.config/havm/config.yml if you want:
vm: cpu_count: 4 memory_size: "4 GiB" disk_size: "32 GiB" network: type: bridge haos: release_channel: stable ssh: authorized_keys: "~/.ssh/id_ed25519.pub" metrics: enabled: true
Prerequisites, installation, first-run walkthrough, and next steps.
CLI reference: run, import-utm, cleanup, version.
Full config.yml reference with every field explained.
Debug SSH setup, graceful shutdown chain, guest IP detection.
Hot-plug coordinators via the menu bar. Entitlements and troubleshooting.
Prometheus endpoint, Grafana dashboard examples, scrape configs.
Build from source, entitlement tiers, architecture overview.
Development workflow, code style, PR checklist, finding issues to work on.