ha: replace the HAOS VM with a native Podman Quadlet container
The fixed 2 GiB ha_van allocation starved the 4 GB Pi. HA Container now runs on the host network (http://10.42.0.1:8123): ha/homeassistant.container installs to /etc/containers/systemd/, config in /srv/homeassistant, host D-Bus mounted for onboard Bluetooth (needs apparmor=unconfined — Ubuntu's dbus-daemon mediates per AppArmor label and denies AddMatch to BlueZ — plus NET_ADMIN/NET_RAW for habluetooth adapter recovery). Drop the VM-era plumbing: the 10.42.0.50 dhcp-host pin becomes a host-record for 10.42.0.1, and the legacy-URL DNAT + hairpin masquerade go away entirely. ha_van.xml retired to git history. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8ab5a4cbd8
commit
3add42a0cb
@@ -61,7 +61,7 @@ This directory is the source of truth. The live system files live under `/etc`,
|
||||
- Cockpit: `https://192.168.10.251:9090` (or `.27`, or ZeroTier). Log in with a Unix account; enable *Administrative access* for action buttons.
|
||||
- Regulatory domain **CA** (unlocks 5GHz ch149–161 @30dBm, no DFS).
|
||||
- ZeroTier network `d3ecf5726d041b2a`, pushed DNS domain `wrede.pvt` via `192.168.196.115` + `192.168.10.5`.
|
||||
- Home Assistant (HAOS in libvirt VM `ha_van`) is bridged into `br0` at **`10.42.0.50:8123`** (pinned DHCP lease, name `homeassistant`); legacy URL `http://10.42.0.1:8123` still works via DNAT.
|
||||
- Home Assistant runs **natively on this host** (Podman Quadlet, host network) at **`http://10.42.0.1:8123`** (name `homeassistant`). The old HAOS VM `ha_van` (`10.42.0.50`) was retired 2026-07-07.
|
||||
|
||||
---
|
||||
|
||||
@@ -139,13 +139,13 @@ This directory is the source of truth. The live system files live under `/etc`,
|
||||
The `hbc` binary itself (venv at `~/venvs/hbd`, symlink `~/bin/hbc`) is installed once via the
|
||||
heartbeat project's own installer — see §4. `deploy.sh` only starts the service once it exists.
|
||||
|
||||
### `ha/` — Home Assistant VM
|
||||
### `ha/` — Home Assistant (native container)
|
||||
| file | → installs to | purpose |
|
||||
|---|---|---|
|
||||
| `ha_van.xml` | *(reference only — `virsh define ha/ha_van.xml` to restore)* | libvirt domain: HAOS VM, virtio NIC bridged onto `br0` (MAC `52:54:00:ad:0a:01`). Disk image lives outside the repo. |
|
||||
| `homeassistant.container` | `/etc/containers/systemd/homeassistant.container` | Podman Quadlet: HA Container on the host network (`:8123`), config in `/srv/homeassistant`, host D-Bus mounted for onboard Bluetooth. |
|
||||
|
||||
Not touched by `deploy.sh` — the VM's LAN address/name come from `ap/van-ap-dnsmasq.conf`
|
||||
(`dhcp-host` → `10.42.0.50`, `homeassistant`) and the legacy-URL DNAT from `ap/nftables.conf`.
|
||||
The `homeassistant` LAN name comes from `ap/van-ap-dnsmasq.conf` (`host-record` →
|
||||
`10.42.0.1`). The retired HAOS VM's domain XML (`ha_van.xml`) lives in git history.
|
||||
|
||||
---
|
||||
|
||||
@@ -206,20 +206,25 @@ Not touched by `deploy.sh` — the VM's LAN address/name come from `ap/van-ap-dn
|
||||
- The Access Points card shows both bands (5GHz `hostapd`, 2.4GHz `hostapd-2g`) with per-band client lists and **Restart** buttons; the radio list lives in `vanrouter.js` (`const APS`).
|
||||
- **Prefer** sets the manual WAN preference (see *Manual preference* above); **Up/Down** connect/disconnect the NM device.
|
||||
|
||||
### Home Assistant VM (`ha_van`)
|
||||
- HAOS runs as a libvirt KVM VM whose NIC is **bridged into `br0`** — it is a first-class LAN
|
||||
device, not NAT'd behind libvirt's `virbr0`. VanLink + wired clients reach it directly at
|
||||
`http://10.42.0.50:8123` (or `http://homeassistant:8123` / `homeassistant.local` via mDNS);
|
||||
ZT clients route in via the ZT-managed `10.42.0.0/24` route. mDNS/SSDP device discovery works
|
||||
because the VM shares the clients' L2 segment.
|
||||
- The old NAT-era URL `http://10.42.0.1:8123` keeps working: nftables DNATs it to `10.42.0.50`,
|
||||
with a hairpin masquerade for same-subnet clients (see comments in `ap/nftables.conf`).
|
||||
- History: the VM used to sit on libvirt's `default` NAT net (`192.168.122.50`) with a
|
||||
`/etc/libvirt/hooks/network` hook inserting FORWARD accepts above libvirt's REJECT. That broke
|
||||
whenever libvirtd re-inserted its chains on restart (hook doesn't fire then) — bridging removed
|
||||
the whole failure mode. The hook and the libvirt DHCP reservation are gone.
|
||||
- Operate: `virsh {start,shutdown,domstate} ha_van`; autostart is per libvirt config. Verify:
|
||||
`curl -s -o /dev/null -w '%{http_code}' http://10.42.0.50:8123/` → `200`.
|
||||
### Home Assistant (native Podman container)
|
||||
- HA Container runs on the host via a **Podman Quadlet** (`ha/homeassistant.container` →
|
||||
`/etc/containers/systemd/`; systemd generates `homeassistant.service`). **Host network**:
|
||||
HA binds `:8123` directly, so clients use `http://10.42.0.1:8123` (or
|
||||
`http://homeassistant:8123`); mDNS/SSDP discovery sees the LAN because there's no bridge
|
||||
or NAT in the way. Config lives in `/srv/homeassistant`.
|
||||
- **Bluetooth**: host BlueZ (`bluetooth.service`) serves the Pi's onboard `hci0` to HA over
|
||||
the mounted `/run/dbus` socket — add the Bluetooth integration in HA and it appears.
|
||||
- No add-on store (that was HAOS's Supervisor): Mosquitto/Zigbee2MQTT-style add-ons become
|
||||
their own containers/services if ever needed.
|
||||
- Operate: `systemctl {status,restart} homeassistant`. Update: bump/pull the image
|
||||
(`podman pull ghcr.io/home-assistant/home-assistant:stable`) and restart. After editing
|
||||
the `.container` file: `./deploy.sh` (or install + `systemctl daemon-reload`), then
|
||||
`systemctl restart homeassistant`. Verify:
|
||||
`curl -s -o /dev/null -w '%{http_code}' http://10.42.0.1:8123/` → `200`.
|
||||
- History: HA started as a HAOS VM on wayback (libvirt NAT, then bridged onto `br0` at
|
||||
`10.42.0.50` with a legacy-URL DNAT), moved to this Pi as an aarch64 VM 2026-07-06, and
|
||||
went native 2026-07-07 — the fixed 2 GiB VM allocation starved the 4 GB Pi. The domain
|
||||
XML (`ha/ha_van.xml`) and the DNAT/hairpin nftables rules are in git history.
|
||||
|
||||
### Never sleep (lid-closed operation)
|
||||
- wayback lives lid-closed in the van and must stay up. Stock logind `HandleLidSwitch=suspend` would sleep it on lid close (even on AC). The `power/10-vanlink-nolid.conf` drop-in sets all three lid actions to `ignore`; `deploy.sh` also masks every sleep target so idle / GUI / a stray `systemctl suspend` can't suspend it.
|
||||
|
||||
@@ -4,20 +4,8 @@
|
||||
table ip van_router_nat
|
||||
delete table ip van_router_nat
|
||||
table ip van_router_nat {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority dstnat; policy accept;
|
||||
# Home Assistant VM (ha_van) is bridged onto br0 at 10.42.0.50 — clients reach
|
||||
# it directly. Keep the legacy http://10.42.0.1:8123 URL working for anything
|
||||
# that bookmarked it (phones, ZT clients).
|
||||
ip daddr 10.42.0.1 tcp dport 8123 dnat to 10.42.0.50:8123
|
||||
}
|
||||
chain postrouting {
|
||||
type nat hook postrouting priority srcnat; policy accept;
|
||||
ip saddr 10.42.0.0/24 oifname != "br0" masquerade
|
||||
# Hairpin for the legacy 10.42.0.1:8123 DNAT when the client sits on the same
|
||||
# subnet as the HA VM: without masquerade the VM would reply directly on br0
|
||||
# from 10.42.0.50 and the client (expecting 10.42.0.1) would drop it. ZT-sourced
|
||||
# traffic doesn't match and doesn't need it — VM replies route back through us.
|
||||
ip saddr 10.42.0.0/24 ip daddr 10.42.0.50 tcp dport 8123 oifname "br0" masquerade
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ domain-needed
|
||||
bogus-priv
|
||||
dhcp-authoritative
|
||||
dhcp-range=10.42.0.10,10.42.0.254,255.255.255.0,12h
|
||||
# Home Assistant VM (libvirt ha_van, bridged onto br0) — pinned address, resolves
|
||||
# as "homeassistant" via this dnsmasq; HAOS also announces homeassistant.local (mDNS).
|
||||
dhcp-host=52:54:00:ad:0a:01,10.42.0.50,homeassistant
|
||||
# Home Assistant runs natively on this host (Podman, host network, :8123) —
|
||||
# "homeassistant" is just another name for the router address.
|
||||
host-record=homeassistant,10.42.0.1
|
||||
dhcp-option=option:router,10.42.0.1
|
||||
dhcp-option=option:dns-server,10.42.0.1
|
||||
|
||||
@@ -54,6 +54,13 @@ if [ ! -f /etc/van-battery/pushover.json ]; then
|
||||
echo " -> seeded /etc/van-battery/pushover.json (EDIT IT: add Pushover token + user)"
|
||||
fi
|
||||
|
||||
echo "== home assistant =="
|
||||
# Native HA (Podman Quadlet, replaced the ha_van VM). daemon-reload below
|
||||
# regenerates homeassistant.service; started (not restarted) at the end so a
|
||||
# deploy never bounces HA — after editing the .container, restart it manually.
|
||||
install -D -m0644 ha/homeassistant.container /etc/containers/systemd/homeassistant.container
|
||||
install -d -m0755 /srv/homeassistant
|
||||
|
||||
echo "== hardware watchdog =="
|
||||
install -D -m0644 power/10-vanlink-watchdog.conf /etc/systemd/system.conf.d/10-vanlink-watchdog.conf
|
||||
|
||||
@@ -68,6 +75,9 @@ systemctl daemon-reexec
|
||||
systemctl mask systemd-networkd-wait-online.service >/dev/null 2>&1 || true
|
||||
systemctl unmask hostapd >/dev/null 2>&1 || true
|
||||
systemctl enable regdomain.service hostapd hostapd-2g van-ap-dnsmasq nftables systemd-networkd van-failover van-thermal van-ap-watchdog van-ap-watchdog-2g >/dev/null 2>&1 || true
|
||||
# bluetooth: host BlueZ serves the onboard hci0 to the HA container over D-Bus
|
||||
systemctl enable --now bluetooth >/dev/null 2>&1 || true
|
||||
systemctl start homeassistant || echo " -> homeassistant failed to start (podman/quadlet — check journalctl -u homeassistant)"
|
||||
systemctl restart van-thermal
|
||||
# Pick up unmanaged-devices changes so NM releases/keeps the right interfaces.
|
||||
nmcli general reload 2>/dev/null || systemctl reload NetworkManager 2>/dev/null || true
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<!-- Home Assistant OS VM — aarch64 port for the Pi 4 "wan" host.
|
||||
Same identity as wayback's x86 ha_van: MAC 52:54:00:ad:0a:01 is pinned to
|
||||
10.42.0.50/"homeassistant" by van-ap-dnsmasq. 2 GiB RAM (Pi has 3.7 total),
|
||||
machine=virt + AAVMF EFI, virtio disk/net. Fresh 18.1 install, configured
|
||||
from scratch (a backup-restore from the x86 instance left Core stopped and
|
||||
Supervisor idle; the old config was minimal, so it was abandoned).
|
||||
VNC graphics + virtio-gpu: the HAOS CLI lives on the graphical console
|
||||
(its serial console runs no getty), so this is the only way in without
|
||||
SSH add-ons — view it via Cockpit's Virtual Machines page. -->
|
||||
<domain type='kvm'>
|
||||
<name>ha_van</name>
|
||||
<uuid>af014c94-de20-4f52-8d6b-438f16cd82e6</uuid>
|
||||
<description>Home Assistant OS (aarch64)</description>
|
||||
<memory unit='KiB'>2097152</memory>
|
||||
<currentMemory unit='KiB'>2097152</currentMemory>
|
||||
<vcpu placement='static'>2</vcpu>
|
||||
<os firmware='efi'>
|
||||
<type arch='aarch64' machine='virt'>hvm</type>
|
||||
<firmware>
|
||||
<feature enabled='no' name='enrolled-keys'/>
|
||||
<feature enabled='no' name='secure-boot'/>
|
||||
</firmware>
|
||||
<boot dev='hd'/>
|
||||
</os>
|
||||
<features>
|
||||
<gic version='2'/>
|
||||
</features>
|
||||
<cpu mode='host-passthrough' check='none'/>
|
||||
<clock offset='utc'/>
|
||||
<on_poweroff>destroy</on_poweroff>
|
||||
<on_reboot>restart</on_reboot>
|
||||
<on_crash>destroy</on_crash>
|
||||
<devices>
|
||||
<emulator>/usr/bin/qemu-system-aarch64</emulator>
|
||||
<disk type='file' device='disk'>
|
||||
<driver name='qemu' type='qcow2' discard='unmap'/>
|
||||
<source file='/var/lib/libvirt/images/haos_generic-aarch64-18.1.qcow2'/>
|
||||
<target dev='sda' bus='scsi'/>
|
||||
</disk>
|
||||
<controller type='scsi' index='0' model='virtio-scsi'/>
|
||||
<interface type='bridge'>
|
||||
<mac address='52:54:00:ad:0a:01'/>
|
||||
<source bridge='br0'/>
|
||||
<model type='virtio'/>
|
||||
</interface>
|
||||
<serial type='pty'>
|
||||
<target type='system-serial' port='0'/>
|
||||
</serial>
|
||||
<console type='pty'>
|
||||
<target type='serial' port='0'/>
|
||||
</console>
|
||||
<graphics type='vnc' port='-1' listen='127.0.0.1'/>
|
||||
<video>
|
||||
<model type='virtio'/>
|
||||
</video>
|
||||
<rng model='virtio'>
|
||||
<backend model='random'>/dev/urandom</backend>
|
||||
</rng>
|
||||
<memballoon model='virtio'/>
|
||||
</devices>
|
||||
</domain>
|
||||
@@ -0,0 +1,34 @@
|
||||
# Home Assistant Container as a Podman Quadlet (replaced the ha_van HAOS VM
|
||||
# 2026-07-07 — the fixed 2 GiB VM allocation starved the 4 GB Pi; VM domain XML
|
||||
# is in git history under ha/ha_van.xml). Installed by deploy.sh to
|
||||
# /etc/containers/systemd/; systemd generates homeassistant.service from it.
|
||||
#
|
||||
# Host networking: HA binds :8123 on the host directly, so the LAN URL is
|
||||
# http://10.42.0.1:8123 (name: homeassistant) — no bridge port, no DNAT.
|
||||
# /run/dbus gives HA the host BlueZ stack = the Pi's onboard Bluetooth (hci0).
|
||||
[Unit]
|
||||
Description=Home Assistant (Podman container)
|
||||
Wants=network-online.target bluetooth.service
|
||||
After=network-online.target bluetooth.service
|
||||
|
||||
[Container]
|
||||
Image=ghcr.io/home-assistant/home-assistant:stable
|
||||
ContainerName=homeassistant
|
||||
Network=host
|
||||
Volume=/srv/homeassistant:/config
|
||||
Volume=/run/dbus:/run/dbus:ro
|
||||
Environment=TZ=America/Toronto
|
||||
# Bluetooth needs two escapes: Ubuntu's dbus-daemon mediates D-Bus per
|
||||
# AppArmor label and the default containers profile can't send to BlueZ
|
||||
# (AddMatch denied), and habluetooth wants NET_ADMIN+NET_RAW for adapter
|
||||
# recovery. HA is rootful + host-net anyway.
|
||||
PodmanArgs=--security-opt apparmor=unconfined
|
||||
AddCapability=NET_ADMIN NET_RAW
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
# First start pulls the image (~600 MB) — allow for a slow uplink.
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user