This is an old revision of the document!
Table of Contents
EVE-OS / ZEDEDA Hardware Model Creation
This page documents how to build a ZEDEDA Cloud hardware model for a new edge node, including a simpler alternative to the USB-round-trip and console/debug-shell methods.
The problem
Two “official” ways exist to pull hardware spec info off a node running EVE-OS:
- USB install method — boot the EVE-OS installer from USB; EVE-OS writes a best-effort model file back to the inventory partition of the USB stick.
- Console + debug shell method — get serial/KVM console access, drop into the EVE-OS debug shell, and run
spec.shmanually.
Both are annoying because of a chicken-and-egg problem: you can't SSH into the node until it's onboarded, and you can't onboard it (with the right model) until you know its hardware model.
The simpler way: SSH-enabled EVE-OS image
You don't actually need onboarding to get SSH. EVE-OS supports a bootstrap SSH config baked into the installer image itself, via files placed on the config partition before flashing. This gets you a root shell over the network the moment the node boots — no console, no USB round-trip, no onboarding.
Prerequisites
- Docker running on your workstation.
- An SSH keypair (
ssh-keygen -t ed25519if you don't have one).
Steps
- Create the config staging directory:
mkdir -p /tmp/config/GlobalConfig
- Drop your public key in as
authorized_keys:
PUBLIC_KEY_PATH=~/.ssh/id_ed25519.pub cat "$PUBLIC_KEY_PATH" > /tmp/config/authorized_keys
- Add the controller FQDN in a
serverfile:
cat <<EOF > /tmp/config/server zedcloud.zededa.net EOF
- Enable SSH via
GlobalConfig/global.json(this file must exist for EVE-OS to load the bootstrap config at all):
cat <<EOF > /tmp/config/GlobalConfig/global.json { "GlobalSettings": { "debug.enable.ssh": { "Key": "debug.enable.ssh", "ItemType": 3, "StrValue": "$(cat "$PUBLIC_KEY_PATH")" } } } EOF
''ItemType: 3'' = string value; ''debug.enable.ssh'' holds the actual public key, not a boolean.
- Build the installer image with the config folder mounted in:
docker run -v /tmp/config:/in --rm lfedge/eve:latest installer_raw > installer-ssh.raw
- Flash
installer-ssh.rawto USB and boot the target device.
- SSH straight in:
ssh -i ~/.ssh/id_ed25519 root@<DEVICE_IP>
Note: this bootstrap SSH access only works before onboarding. Once the node onboards, these settings are ignored. If you want to keep SSH access afterward, re-enable it through ZCLI:
zcli edge-node update <edge-node-name> --config="debug.enable.ssh:$(cat ~/.ssh/id_ed25519.pub)"
Gathering the hardware spec over SSH
Once you have a shell, pull the same information the console/debug-shell method would give you, without needing physical access at all.
Get the best-effort model JSON
spec.sh
This produces a JSON blob (CPU count, memory, storage, and an ioMemberList of guessed adapters — VGA, USB, COM ports, NVMe, Ethernet, etc.). Save it, e.g. as MyModel.json. Treat it as a draft — EVE-OS's guesses are “best effort” and usually need correcting.
Validate/correct each adapter
- Remove logo placeholders —
logo_back/logo_frontdefault to placeholder paths; blank them out or you'll get a cosmetic error. - PCI devices (VGA, USB, Ethernet, etc.):
lspci -k
- IOMMU groupings (which devices share a passthrough group — matters for
assigngrp):
find /sys/kernel/iommu_groups/ -type l
- Serial/COM ports:
dmesg | grep -i ttyS
- USB topology (to confirm which physical port maps to which controller):
lsusb
lsusb -t
Cross-check each entry in the ioMemberList against this output:
- Fix or fill in empty
PciLong/Ioports/assigngrpvalues. - Delete entries for ports that don't actually exist as separate controllers (e.g., a device with 4 physical USB ports but only 1-2 controllers in
lspci/iommu output — the extra logical entries should be removed since they can't be independently passed through). - Omit adapters you don't intend to model (e.g., NVMe, if not relevant).
Validate the final file with a JSON linter before uploading.
Uploading the model to ZEDEDA Cloud (ZCLI)
- Copy the validated JSON into the ZCLI docker container:
docker cp /path/to/MyModel.json docker_containerID:/home/zcli
- Create the hardware brand, if it doesn't already exist:
zcli brand create NAME --title=TITLE
- Create the model, linking it to the JSON:
zcli model create NAME --hardware-details=PATH/TO/JSON --brand=BRAND [--title=TITLE]
- To revise a model later, edit the JSON and:
zcli model update NAME [--hardware-details=PATH/TO/JSON] [--brand=BRAND] [--title=TITLE]
Changes propagate to every edge node currently using that model.
Onboarding the node
With the model created, onboard the edge node in ZEDEDA Cloud (GUI or ZCLI): pick the Brand/Model, choose a Hardware Identity method (Edge Node Certificate from the USB inventory partition, Onboarding Key + serial number, or a single-use installer), and finish the flow.
Good to know: for lab/test nodes, ZEDEDA Cloud lets you onboard with *any* placeholder model just to get connectivity up — you don't strictly need the final model in hand before the first boot. That's a second way to sidestep the chicken-and-egg problem: onboard with a generic model + Onboarding Key/soft-serial (no cert extraction needed at all), then once online, correct/replace the model later. The SSH-image method above is still preferable when you want the *real* model nailed down before onboarding at all, e.g. for image reuse across a fleet of identical devices.
Method comparison
| Method | Needs console/KVM | Needs USB round-trip | Needs onboarding first | Notes |
|---|---|---|---|---|
| USB install dump | No | Yes | No | Best-effort model written back to USB inventory partition |
| Console + spec.sh | Yes | No | No | Manual, slow, needs physical/remote KVM access |
| SSH-enabled image | No | No (still need to flash once) | No | Recommended — full shell access pre-onboarding |
| Onboard w/ generic model | No | No | N/A (this *is* onboarding) | Fastest to get connectivity; still need to fix model later |
