<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Shelly on dKade's notes</title><link>https://dkade.com/tags/shelly/</link><description>Recent content in Shelly on dKade's notes</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>dkade@dkade.com (Daniel Loureiro)</managingEditor><webMaster>dkade@dkade.com (Daniel Loureiro)</webMaster><lastBuildDate>Fri, 19 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://dkade.com/tags/shelly/index.xml" rel="self" type="application/rss+xml"/><item><title>Running HomeAssistant Core in FreeBSD Jails with BastilleBSD</title><link>https://dkade.com/posts/homeassistant_freebsd_jail/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><author>dkade@dkade.com (Daniel Loureiro)</author><guid>https://dkade.com/posts/homeassistant_freebsd_jail/</guid><description>A complete guide to running HomeAssistant Core in a VNET FreeBSD jail managed with BastilleBSD, with separate jails for Mosquitto MQTT and Zigbee2MQTT with USB passthrough</description><content:encoded><![CDATA[<p>I run HomeAssistant Core on FreeBSD inside a jail managed with BastilleBSD — no Docker, no VM, just a lightweight VNET jail with its own IP on the LAN. This post covers the full setup: three jails, a bootstrap script, USB passthrough for Zigbee, and WiFi devices on the same subnet.</p>
<h2 id="architecture">Architecture</h2>
<pre tabindex="0"><code>FreeBSD 15.1-RELEASE Host
│
├── homeassistant (192.168.2.50)  — HA Core :8123
├── mosquitto    (192.168.2.52)  — MQTT broker :1883
└── zigbee2mqtt  (192.168.2.51)  — Zigbee2MQTT, Sonoff USB passthrough
</code></pre><p>All three jails are VNET on the same <code>/24</code> subnet. HA Core talks to Mosquitto over MQTT, Zigbee2MQTT publishes device states to Mosquitto, and HA subscribes. Shelly devices connect directly over WiFi and are auto-discovered by HA on the LAN.</p>
<h2 id="why-jails">Why jails?</h2>
<ul>
<li>Native FreeBSD — no Docker overlay, no Linux emulation</li>
<li>VNET gives each jail a real IP on the LAN — devices discover HA directly</li>
<li>ZFS snapshots for quick rollbacks before upgrades</li>
<li>Each service in its own jail — clean isolation, restart one without affecting others</li>
</ul>
<h2 id="creating-the-jails">Creating the jails</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bastille create -V homeassistant 15.1-RELEASE 192.168.2.50/24 vtnet0
</span></span><span class="line"><span class="cl">bastille create -V mosquitto 15.1-RELEASE 192.168.2.52/24 vtnet0
</span></span><span class="line"><span class="cl">bastille create -V zigbee2mqtt 15.1-RELEASE 192.168.2.51/24 vtnet0
</span></span></code></pre></div><p>The <code>-V</code> flag gives each jail a VNET interface with its own IP and routing table — they live on the same broadcast domain as the host.</p>
<h2 id="homeassistant-jail">HomeAssistant jail</h2>
<p>A bootstrap script handles the full setup:</p>
<ul>
<li><strong>Repository</strong>: <a href="https://codeberg.org/dkade/BSD/src/branch/main/FreeBSD/install_ha_freebsd.sh">codeberg.org/dkade/BSD</a></li>
<li><strong>Method</strong>: Python 3.14 virtual environment under <code>/usr/local/etc/homeassistant/venv</code></li>
<li><strong>User</strong>: dedicated <code>hass</code> system user</li>
<li><strong>Service</strong>: FreeBSD rc.d script using <code>daemon -u hass</code></li>
<li><strong>Enable</strong>: <code>sysrc homeassistant_enable=YES</code></li>
</ul>
<p>What the script does:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Inside the jail as root</span>
</span></span><span class="line"><span class="cl">fetch https://codeberg.org/dkade/BSD/raw/branch/main/FreeBSD/install_ha_freebsd.sh
</span></span><span class="line"><span class="cl">sh install_ha_freebsd.sh
</span></span></code></pre></div><p>It installs system packages (<code>python314</code>, <code>gcc12</code>, <code>rust</code>, <code>dbus</code>, <code>libffi</code>, <code>openssl</code>, etc.), creates a venv, and installs <code>homeassistant</code> along with <code>numpy</code>, <code>zlib_ng</code>, and <code>isal</code> for performance. The rc.d script starts HA as the <code>hass</code> user and listens on port 8123.</p>
<p>After first boot, complete the onboarding at <code>http://192.168.2.50:8123</code>.</p>
<h2 id="mosquitto-jail">Mosquitto jail</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bastille console mosquitto
</span></span><span class="line"><span class="cl">pkg install mosquitto
</span></span><span class="line"><span class="cl">sysrc <span class="nv">mosquitto_enable</span><span class="o">=</span>YES
</span></span><span class="line"><span class="cl">service mosquitto start
</span></span></code></pre></div><p>No authentication is needed on a local subnet — configure <code>listener 1883 192.168.2.52</code> in <code>/usr/local/etc/mosquitto/mosquitto.conf</code> to bind to the jail IP.</p>
<h2 id="zigbee2mqtt-jail">Zigbee2MQTT jail</h2>
<p>This jail needs access to the Sonoff Zigbee coordinator plugged into the host.</p>
<h3 id="usb-passthrough-with-devfs">USB passthrough with devfs</h3>
<p>On the <strong>host</strong> (<code>/etc/devfs.rules</code>):</p>
<pre tabindex="0"><code>[bastille_usb=101]
add path &#39;usbctl&#39; mode 0660
add path &#39;usb/*&#39; mode 0660
add path &#39;ugen*&#39; mode 0660
add path &#39;cuaU*&#39; mode 0660
add path &#39;ttyU*&#39; mode 0660
</code></pre><p>Set the rule in <code>/usr/local/bastille/jails/zigbee2mqtt/jail.conf</code>:</p>
<pre tabindex="0"><code>devfs_ruleset = &#34;bastille_usb&#34;;
</code></pre><p>Then find the Sonoff device:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># On the host</span>
</span></span><span class="line"><span class="cl">ls /dev/cuaU*
</span></span><span class="line"><span class="cl"><span class="c1"># /dev/cuaU0 &lt;- this is the Sonoff</span>
</span></span></code></pre></div><p>Restart the jail and you should see the device inside:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bastille restart zigbee2mqtt
</span></span><span class="line"><span class="cl">bastille console zigbee2mqtt
</span></span><span class="line"><span class="cl">ls /dev/cuaU*
</span></span></code></pre></div><h3 id="installing-and-configuring">Installing and configuring</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bastille console zigbee2mqtt
</span></span><span class="line"><span class="cl">pkg install node npm mosquitto-libs
</span></span><span class="line"><span class="cl">npm install -g zigbee2mqtt
</span></span></code></pre></div><p>Create <code>/usr/local/etc/zigbee2mqtt/configuration.yaml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">mqtt</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">mqtt://192.168.2.52:1883</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">serial</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="l">/dev/cuaU0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">frontend</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">8080</span><span class="w">
</span></span></span></code></pre></div><p>rc.d script at <code>/usr/local/etc/rc.d/zigbee2mqtt</code> to start via <code>daemon</code>, then <code>sysrc zigbee2mqtt_enable=YES &amp;&amp; service zigbee2mqtt start</code>.</p>
<h2 id="device-integration">Device integration</h2>
<ul>
<li><strong>Shelly</strong> (WiFi): connect them to your WiFi. HA auto-discovers them via the LAN once the Shelly integration is added in the UI.</li>
<li><strong>Zigbee</strong> (Sonoff sensors, bulbs, etc.): pair through the Zigbee2MQTT frontend (<code>http://192.168.2.51:8080</code>). HA receives device states via MQTT auto-discovery.</li>
</ul>
<h2 id="upgrading">Upgrading</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># HA Core</span>
</span></span><span class="line"><span class="cl">bastille console homeassistant
</span></span><span class="line"><span class="cl">service homeassistant stop
</span></span><span class="line"><span class="cl">su -l hass -c <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s1">&#39;HOME=/usr/local/etc/homeassistant PIP_CACHE_DIR=/tmp/pip-cache \
</span></span></span><span class="line"><span class="cl"><span class="s1">   /usr/local/etc/homeassistant/venv/bin/pip install --upgrade homeassistant&#39;</span>
</span></span><span class="line"><span class="cl">service homeassistant start
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># zigbee2mqtt</span>
</span></span><span class="line"><span class="cl">bastille console zigbee2mqtt
</span></span><span class="line"><span class="cl">service zigbee2mqtt stop
</span></span><span class="line"><span class="cl">npm update -g zigbee2mqtt
</span></span><span class="line"><span class="cl">service zigbee2mqtt start
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># mosquitto</span>
</span></span><span class="line"><span class="cl">bastille console mosquitto
</span></span><span class="line"><span class="cl">pkg upgrade mosquitto
</span></span><span class="line"><span class="cl">service mosquitto restart
</span></span></code></pre></div><h2 id="gotchas">Gotchas</h2>
<ul>
<li><strong>Python compilation</strong>: HA Core compiles C and Rust extensions during <code>pip install</code>. Make sure <code>gcc12</code>, <code>rust</code>, <code>libffi</code>, and <code>openssl</code> are installed in the jail before installing HA.</li>
<li><strong>dbus</strong>: HA Core expects D-Bus to be running (<code>service dbus start</code>). The rc.d script has <code>REQUIRE: dbus</code>, so dbus starts first.</li>
<li><strong>USB devfs</strong>: The devfs ruleset must be applied <em>before</em> the jail starts. Double-check the ruleset number matches in both <code>/etc/devfs.rules</code> and the jail config.</li>
<li><strong>VNET routing</strong>: If jails can&rsquo;t reach the internet for <code>pkg install</code>, check that <code>gateway_enable=&quot;YES&quot;</code> is set on the host and NAT/pf is configured for the VNET subnet.</li>
<li><strong>Persistent pip cache</strong>: The script sets <code>PIP_CACHE_DIR=/tmp/pip-cache</code> so you don&rsquo;t re-download wheels on every upgrade.</li>
</ul>
<h2 id="summary">Summary</h2>
<p>Three FreeBSD jails managed with BastilleBSD running HA Core, Mosquitto, and Zigbee2MQTT — each with its own IP, rc.d service, and clean isolation. The bootstrap script at <code>codeberg.org/dkade/BSD</code> automates the HA jail from scratch, and the rest is just standard FreeBSD package management.</p>
<hr>
<p><em>Disclaimer: I use AI as a productivity tool. For a senior engineer, AI is incredibly powerful as one can focus on the solution design and conceptualization and leave the boring part that is implementation to the AI.</em></p>
]]></content:encoded></item></channel></rss>