Dobby·Guide github.com/mhyrr/dobby

A place for Dobby to live

You can meet Dobby on a laptop. A house elf needs somewhere permanent to live.

A household service needs a machine that stays on. This page is in two halves. The first half is the hardware, which is still a proposal. The second half is the installed service, which was walked on 2026-09-04 on a stand-in for the box: a Debian 12 virtual machine, installed from the tarball, rebooted, and upgraded.

The hardware is a proposal, not a walk Nobody has bought this box and installed both halves. The parts and prices below are from research done in September 2026. The service half of this page is walked; the hardware half will be rewritten when the box exists.

What each half needs

Home Assistant wants a machine of its own. Home Assistant OS manages its own updates, add-ons, and backups, and you cannot install other software into it. Give it a small box or a virtual machine.

Dobby is an ordinary Linux service. The box needs PostgreSQL, Avahi for the dobby.local name, and the release tarball, which carries its own Erlang. No Elixir, no git, no build tools. It needs about 300 MB of memory when running, no GPU, and an outbound HTTPS connection to the model provider.

One box: a mini PC running Proxmox

This is the layout Dobby was designed for. A small x86 mini PC runs Proxmox VE, a free hypervisor. Each half gets a virtual machine.

PartProposedWhy
BoxIntel N150 mini PC with 16 GB and a 500 GB NVMe: a Beelink EQ14 or a GMKtec NucBox G3 Plus, about $190 in September 2026. The N100 is the same chip in practice; buy whichever is cheaper.About 6 W idle, near silent, a well-worn Proxmox host. 16 GB is enough; 32 GB is comfortable, and a single 32 GB SODIMM works on most of these boards.
HypervisorProxmox VE 9.2.Backups of both machines are one scheduled vzdump.
VM 1: Home AssistantHAOS 18 from the official image, by the community-scripts helper or by importing the qcow2 by hand. 2 vCPU, 4 GB.HAOS in its own VM, so add-ons and updates work as designed.
VM 2: DobbyDebian 13. 2 vCPU, 4 GB. Dobby under /opt/dobby, PostgreSQL, Avahi.A plain Linux host you can ssh into. A virtual machine and not a container, because the name needs multicast and containers on the Proxmox bridge fight for it.
RadiosA Zigbee, Thread, or Z-Wave USB stick, passed through to the HA VM by USB vendor and device id, never by port.Home Assistant owns the radios. Dobby never talks to one. A stick passed through by port disappears when the host enumerates it differently.
PowerA small UPS on the Proxmox host, watched by NUT in netserver mode. An APC BE600M1 is about $84 and runs this load for more than an hour.The host has to shut both machines down cleanly. Pass the UPS into the HA VM and the host never learns the power failed.

Both VMs sit on the home LAN. Nothing is forwarded in from the internet. The only traffic that leaves is Dobby's call to the model provider. To reach the box from away, use a mesh VPN on the Dobby VM, not a port forward.

All in, about $275 and 10 to 15 W at the wall. This is the recommendation. The reasons are the isolation, one backup for both machines, and the price: the memory shortage of 2026 doubled the cost of every small ARM board and appliance, and a mini PC on commodity DDR4 did not move.

Two boxes: an appliance and a helper

If Home Assistant already runs on its own hardware, keep it. Dobby needs one more small machine on the same network.

Two boxes cost about $360. In exchange, Home Assistant stays an appliance you never log into, and you can break Dobby's machine without touching the house's automations.

Not a Home Assistant add-on

Dobby could be packaged as a Home Assistant add-on, one container beside the others, folded into Home Assistant's own backups. It was considered and rejected. An add-on cannot publish dobby.local: the HAOS host already runs an mDNS responder and a second one collides with it. The host is Buildroot, with no apt and no systemd unit of your own, and a shell into the host takes a USB stick and a debug port. Dobby's debugging surface is a live shell into the running node, and an add-on has no clean way to offer one.

Home Assistant Supervised, Home Assistant's own Supervisor on a Debian host, ended support at Home Assistant 2025.12 and is not a foundation for a box meant to run for a year.

What you do not need

The installed service, walked

This section is a record of 2026-09-04. The stand-in was a Debian 12.12 virtual machine on arm64, with Home Assistant reached through a tunnel to the laptop that runs the development rig. Everything below is what was typed and what came back. Where the box will differ, the text says so.

The tarball

Every tagged version publishes two tarballs on the GitHub releases page: dobby-<version>-linux-amd64.tar.gz for an Intel or AMD box and dobby-<version>-linux-arm64.tar.gz for a Pi or an ARM machine. Both are built on Debian 12 by rel/Dockerfile, on a runner of the matching architecture, so nothing is emulated. To build one yourself, on any machine with Docker:

docker build -f rel/Dockerfile --output dist .

The walk used two builds of the same branch, numbered 0.1.0 and 0.1.1, because there was no second version yet. The first tagged release is v0.1.0.

Prepare the box

PostgreSQL, Avahi, a role and a database, a service user, and the directories. Once.

sudo apt install postgresql avahi-daemon avahi-utils
sudo -u postgres psql -c "CREATE ROLE dobby LOGIN PASSWORD 'choose-one';"
sudo -u postgres psql -c "CREATE DATABASE dobby OWNER dobby;"
sudo useradd --system --home-dir /opt/dobby --shell /usr/sbin/nologin dobby
sudo mkdir -p /opt/dobby/releases /opt/dobby/config
sudo mkdir -p /opt/dobby/data/backups /opt/dobby/data/tmp
sudo chown -R dobby:dobby /opt/dobby/config /opt/dobby/data

The release directories stay root's. The service cannot rewrite its own code; it writes only under data.

The two files, and the environment

Put the house file at /opt/dobby/config/home.yaml and the soul at /opt/dobby/config/soul.md. Set lan: true in the house file's system section, so the household can reach the box. Then the service environment, which is every credential the house has:

sudo cp /opt/dobby/current/box/dobby.env.example /opt/dobby/config/dobby.env   # after the first untar below
sudo chown dobby:dobby /opt/dobby/config/dobby.env && sudo chmod 600 /opt/dobby/config/dobby.env
openssl rand -base64 48                                                      # the SECRET_KEY_BASE line

Fill in DATABASE_URL with the password you chose, SECRET_KEY_BASE with the generated value, DOBBY_HA_URL and DOBBY_HA_TOKEN for Home Assistant, and OPENROUTER_API_KEY. The file is read by systemd: one KEY=value per line, no export. Keep the secret key base. Change it and every browser in the house is asked its name again.

Install and start

The walk used 0.1.0, so every path here says 0.1.0. The tarball on the releases page is named for the current version, and that number goes in its place.

sudo mkdir -p /opt/dobby/releases/0.1.0
sudo tar -xzf dobby-0.1.0-linux-arm64.tar.gz -C /opt/dobby/releases/0.1.0
sudo ln -sfn /opt/dobby/releases/0.1.0 /opt/dobby/current
sudo cp /opt/dobby/current/box/dobby.service /etc/systemd/system/
sudo systemctl enable --now dobby

What you should see

$ systemctl status dobby ● dobby.service - Dobby, the household agent Loaded: loaded (/etc/systemd/system/dobby.service; enabled; preset: enabled) Active: active (running) since Fri 2026-09-04 18:10:30 EDT; 2s ago Process: 8930 ExecStartPre=/opt/dobby/current/bin/migrate (code=exited, status=0/SUCCESS) Main PID: 8986 (beam.smp) $ journalctl -u dobby -o cat [info] == Running 20260814150950 Dobby.Repo.Migrations.CreateSchedules.change/0 forward ... [info] == Migrated 20260821180000 in 0.0s Started dobby.service - Dobby, the household agent. [info] Running DobbyWeb.Endpoint with Bandit 1.12.4 at 0.0.0.0:4000 (http) [info] the house answers at http://dobby.local:4000/ (192.168.5.15) [info] connected to Home Assistant 2026.8.2 at http://127.0.0.1:8123

The unit runs the migrations before every start, so the first start is also the first migration. Then the endpoint, the name, and Home Assistant. On the stand-in, Home Assistant was the laptop's, through a tunnel at 127.0.0.1; on the box it is the address in DOBBY_HA_URL. Open http://dobby.local:4000/ from a phone. The three pages load and the thread connects.

The journal also carries a stack of warnings at every boot about the house's model not being in the catalog the model library ships with. They are noise, and they are a known open item.

Reboot

Pull the plug, or sudo reboot. The unit is enabled, so the service comes back with the box.

What you should see

$ uptime -s 2026-09-04 18:15:37 $ systemctl show dobby -p ActiveEnterTimestamp ActiveEnterTimestamp=Fri 2026-09-04 18:15:43 EDT $ journalctl -b -u dobby -o cat [info] Migrations already up Started dobby.service - Dobby, the household agent. [warning] Home Assistant unreachable at http://127.0.0.1:8123 (%Mint.TransportError{reason: :econnrefused}); retrying in 1000ms [info] Running DobbyWeb.Endpoint with Bandit 1.12.4 at 0.0.0.0:4000 (http) [warning] Home Assistant unreachable at http://127.0.0.1:8123 (%Mint.TransportError{reason: :econnrefused}); retrying in 16000ms [info] connected to Home Assistant 2026.8.2 at http://127.0.0.1:8123

Six seconds from kernel to house. The Home Assistant warnings are the stand-in's: the reboot killed the tunnel, and the client backed off from one second to sixteen until the tunnel came back, then reconnected on its own. On the box, Home Assistant is another machine that may also be booting, and the same backoff covers it.

Upgrade

Untar the new version beside the old one, repoint current, restart. The restart migrates first.

sudo mkdir -p /opt/dobby/releases/0.1.1
sudo tar -xzf dobby-0.1.1-linux-arm64.tar.gz -C /opt/dobby/releases/0.1.1
sudo ln -sfn /opt/dobby/releases/0.1.1 /opt/dobby/current
sudo systemctl restart dobby

What you should see

Stopping dobby.service - Dobby, the household agent... Stopped dobby.service - Dobby, the household agent. [info] == Running 20260820120000 Dobby.Repo.Migrations.CreateDeviceProposals.change/0 forward [info] create table device_proposals [info] create index device_proposals_outstanding_index [info] create index device_proposals_status_index [info] == Migrated 20260820120000 in 0.0s [info] == Running 20260821180000 Dobby.Repo.Migrations.CreateMcpTokens.change/0 forward [info] create table mcp_tokens [info] == Migrated 20260821180000 in 0.0s Started dobby.service - Dobby, the household agent. [info] connected to Home Assistant 2026.8.2 at http://127.0.0.1:8123 [info] Running DobbyWeb.Endpoint with Bandit 1.12.4 at 0.0.0.0:4000 (http) [info] the house answers at http://dobby.local:4000/ (192.168.5.15)

Two seconds of stop, the pending migrations, then the house. The walk had rolled the last two migrations back by hand before this restart, so that the upgrade had migrations to run; a real upgrade with a new migration takes the same path. If a migration fails, the service does not start and journalctl -u dobby says why. The old release stays under releases until you remove it.

Rolling a migration back

By hand only, as the service user, with the service environment loaded. Nothing rolls back on its own.

sudo -u dobby bash -c 'set -a; . /opt/dobby/config/dobby.env; set +a;
  RELEASE_TMP=/opt/dobby/data/tmp /opt/dobby/current/bin/dobby eval "Dobby.Release.rollback(Dobby.Repo, 20260820120000)"'

The number is the migration to roll back to, inclusive; the walk's rollback to 20260820120000 undid that migration and the one after it. The same shape runs anything else in the release: bin/dobby remote opens a live shell into the running house.

Where things are

/opt/dobby/
├── releases/
│   ├── 0.1.0/                  the untarred release, root's, carries its own Erlang
│   └── 0.1.1/
│       └── box/                dobby.service and dobby.env.example, inside every tarball
├── current -> releases/0.1.1
├── config/                     the household's, and all it ever edits
│   ├── home.yaml
│   ├── soul.md
│   └── dobby.env               mode 600: the database, the secret, the two tokens
└── data/
    ├── tmp/                    the release's boot files
    └── backups/                owed: see below

Editing the house or the soul is sudo systemctl restart dobby. Nothing under config is inside a release, so an upgrade never touches it.

Port 80

The unit grants the service the capability to bind port 80, so port: 80 in the house file's system section is enough for a bare http://dobby.local/. Not walked: the stand-in ran on 4000.

Not yet walked The name. The stand-in's network was the laptop's own, so no phone resolved dobby.local to it; the name was published and resolved on the box itself. The first boot on the stand-in also found the Linux name publisher had never run on a real Debian host and collided with the daemon's own address record. That is fixed in 0.1.0. Backup and restore are owed.

Then what

With the box up, write down the house, verify every device from the development checkout, and open dobby.local from the kitchen. Backup and restore, a pg_dump beside the two files on a schedule and a restore onto a fresh box, are the next walk and will land under data/backups.

Prices and parts, September 2026: Raspberry Pi on the memory-driven price rises · Nabu Casa on Green pricing · the HAOS VM helper for Proxmox · a UPS on the Proxmox host · mDNS inside an add-on.