Every driver
OPC UA, Modbus, MQTT and Sparkplug B, EtherNet/IP, S7comm, MTConnect, HTTP and UniFi, from the same code the Gateway polls with.
A headless edge node for a Pi or a small industrial PC. It polls equipment with the same drivers the Gateway uses, forwards every reading over Sparkplug B, and buffers to disk when the uplink goes away. No database, no UI, no login, and no way to write to a machine. That's the feature: a box with nothing to click can't be clicked wrong.
The image is ghcr.io/hotloop-io/hotloop-edge-relay:4.18.10 for amd64, arm64 and arm/v7, about 15 MB, and it pulls with no login. The
chart is hotloop-edge-relay from https://hotloop.io/hotloop, and there is a Quadlet unit
for a box that isn't in a cluster.
It's a different binary. It never links the database, the web UI, the automation engine, the entity registry or MCP, and a test asks the compiler what actually got linked and fails the build if any of them sneaks in. There's no "relay mode" buried in the Gateway, because a mode is one bad environment variable away from a web server listening on a box nobody's watching.
OPC UA, Modbus, MQTT and Sparkplug B, EtherNet/IP, S7comm, MTConnect, HTTP and UniFi, from the same code the Gateway polls with.
Sparkplug B births, sequence numbers and deaths. When the link drops it queues to one file on disk and replays in order, flagged historical.
Devices and tags come from one JSON file. A bad file is refused and the old one keeps running. A pushed file applies the moment it lands.
The relay listens to MQTT brokers for ESPHome, Zigbee2MQTT, Tasmota and Shelly devices, read and forward only. It still can't write to a thing.
It reads and forwards. It can't take a write, serve a screen or answer an agent, because it has no users to pin an action on.
The Gateway's fleet API tracks every relay from its births and deaths, and sends a rebirth, a restart or a new device file over the connection the relay dialed out on.
Run end to end against a real Mosquitto broker, Modbus TCP slaves, an HTTP endpoint and an OPC UA test server. Not yet run on real EtherNet/IP or S7comm hardware.
OPC UA, Modbus, MQTT and Sparkplug B, EtherNet/IP, S7comm, MTConnect, HTTP and UniFi. The same code the Gateway polls with, down to the reconnect and back-off, so a bug fixed there is fixed here.
Births, sequence numbers and deaths the way Sparkplug B says. When the link drops it queues to one file on disk and replays in order, flagged historical, when it's back. The historian upstream gets the gap filled instead of a hole.
Devices and tags come from one JSON file, and a file the Gateway pushes is read at start. A bad file is refused and the old one keeps running. A pushed file also applies the moment it lands: new devices connect, removed ones let go, changed ones reconnect, and the ones nobody touched keep their sessions. The Sparkplug session stays up with no death certificate, and a removed metric goes out once as null so the Gateway stops showing its last good value forever.
It reads and forwards. It can't take a write, serve a screen or answer an agent, because every one of those needs a signed-in person to pin the action on, and it has no users at all.
The Gateway's fleet API (no screen yet) tracks every relay you register from its births and deaths, and sends a rebirth, a restart or a new device file over the connection the relay already dialed out on. Every birth also carries the relay's version and image digest, so the fleet list shows what each one runs, and an admin can approve an update a relay is holding. A pushed file lands on the relay's volume and wins over the chart's devices from 4.17.0 on; a 4.16.0 relay dropped it on the floor. Commands need no way in to the edge box, and nobody drives out to it.
Run end to end against a real Mosquitto broker, polling an HTTP endpoint, Modbus TCP slaves and an OPC UA test server, with live config reload proven the same way. Discovery is proven against recordings of real devices, not a bench of them. It hasn't been run against real EtherNet/IP or S7comm hardware or a certified Sparkplug host yet. Until it has, this card stays here.
Two supported ways, both containers, both start on boot and come back when they die. A bare
podman run from an SSH session is not one of them, because it dies the moment you close
the laptop, and you'll find out from the historian a week later.
The broker and the Sparkplug group are required, and the chart refuses to render without them.
helm repo add hotloop https://hotloop.io/hotloop
helm repo update
helm install press-line-3 hotloop/hotloop-edge-relay \
--version 4.18.10 --namespace hotloop --create-namespace \
--set edgePublish.broker=tcp://gateway.plant.local:1883 \
--set edgePublish.group=Plant1 \
-f devices.yaml
Devices and tags go in devices.yaml under devices: and tags:. If a
device carries a password, put the whole device file in a Secret and name it in
devicesSecret, because values end up in a ConfigMap anyone in the namespace can read.
For a Pi or an industrial PC outside any cluster. Quadlet turns the container into a systemd service:
it starts on boot with nobody logged in, restarts when it dies, and logs to the journal. It needs Podman
5.0 or newer, so Raspberry Pi OS Bookworm, which ships 4.3, is too old for it. The unit follows
:stable and rolls a bad update back.
Save edge-relay.container and edge-relay-data.volume from the
install docs into
/etc/containers/systemd/, set the broker, group and node in the first, and put your
device file at /etc/edge-relay/devices.json. Then:
printf '%s' 'your-broker-password' | \
sudo podman secret create edge-relay-broker-password -
sudo systemctl daemon-reload
sudo systemctl start edge-relay.service
curl localhost:8081/status
Broker takes anonymous connections? Skip the secret and delete the unit's Secret= line. On an
isolated OT network that's a legit setup, not a mistake.
The Edge Relay is under the HotLoop Business Source License 1.1, like the rest of the lineup. Home and lab use is free. Business use is free through EmberNET. Support, SLAs, and warranties come from our Official Systems Integrator, Fireball Industries.