IoT Gateway with Composable Containers
What an IoT Gateway Needs
A typical IoT or industrial gateway sits at the boundary between field-side equipment (sensors, PLCs, machines) and the cloud. It needs:
- Stable BSP for the chosen hardware (RPi, NXP, TI, etc.)
- Networking (Ethernet, WiFi, LTE, sometimes LoRa)
- Secure remote access (VPN, mesh networking)
- Protocol adapters (Modbus, OPC-UA, CAN, BACnet, custom serial)
- Local broker / buffer (MQTT, Kafka client, store-and-forward)
- Edge processing (filtering, aggregation, anomaly detection)
- OTA updates for every layer above
- Auditable, recoverable updates for compliance and uptime
Building this as a monolithic image means every change to one piece reflashes the whole device. Composable containers fix that.
Pantavisor’s Gateway Composition
Each gateway is a state composed of independent containers:
my-iot-gateway/ (state JSON)
├── bsp/ # Kernel, modules, firmware (RPi 4 ARMv8)
├── platform/ # Alpine + ConnMan (networking)
├── tailscale/ # Mesh VPN container
├── modbus/ # Industrial protocol adapter
├── mqtt-broker/ # Local MQTT broker (Mosquitto)
├── edge-app/ # Custom edge processing
└── pvr-sdk/ # Optional management container (dev/diagnostics)Each container:
- Is built and versioned independently.
- Has its own
args.json,config.json, and overlay configs in_config/<container>/. - Declares dependencies on other containers via
PV_SERVICES_REQUIRED. - Has its own
status_goalso failures are caught and rolled back automatically.
Why Composability Matters Here
Different teams, different cadences
The kernel team may release a quarterly BSP update. The networking team patches ConnMan when a CVE drops. The edge-app team ships weekly. With monolithic firmware, every cadence collides. With containers, each team owns its piece and ships independently.
Different SKUs, shared base
A “WiFi-only gateway” and a “WiFi + LTE gateway” can share BSP, platform, MQTT, and edge-app, differing only in the modem container. No fork. No parallel build pipelines.
Per-customer customization
Customer A wants Modbus + S3 export. Customer B wants OPC-UA + Azure IoT Hub. Two custom containers per customer, everything else shared. Pantahub channels deliver the right composition to the right devices.
Building One — Concrete Example
Using the Yocto path (see Build an Embedded Linux Image from Containers):
SUMMARY = "Industrial IoT Gateway"
LICENSE = "MIT"
inherit image pvroot-image
PVROOT_CONTAINERS_CORE ?= "\
pv-alpine-connman \\ # networking
pv-tailscale \\ # mesh VPN
pv-mqtt-broker \\ # local MQTT
edge-app \\ # custom processing
"
PVROOT_CONTAINERS ?= "\
modbus-adapter \\ # optional, install per-customer
opcua-adapter \\
"
PVROOT_IMAGE_BSP ?= "core-image-minimal"Build, flash, boot — the gateway comes up with all core services running. Optional containers are factory-bundled and installed on first boot per device metadata.
Adding Tailscale Mesh VPN
A common gateway requirement is zero-config remote access. Wrap an existing Tailscale Docker image as a Pantavisor container:
pvr app add tailscale --from=docker://tailscale/tailscale:latest \
--args-json=tailscale-args.jsonThe container runs alongside everything else, exposes the Tailscale interface to other containers via shared network namespace or host networking, and updates independently of the rest of the stack.
Per-Container Resource Limits
Each container can declare CPU, memory, and I/O limits in its run.json / LXC config:
- Edge-app gets the lion’s share of CPU.
- Modbus poller gets a small slice with high priority.
- Logging container is capped so a runaway log doesn’t OOM the gateway.
Failure Isolation
A crashing edge-app container restarts (per its restart_policy) without affecting MQTT broker, Tailscale, or the BSP. A failed update to the Modbus container rolls back without touching the rest of the composition.
This is the practical difference between “containers” and “monolith”: one bad component doesn’t take down the whole gateway.
OTA per Container
# Update only the protocol adapter
pvr clone https://pvr.pantahub.com/<USER>/<DEVICE_NICK> ws
cd ws
pvr app update modbus --from=https://gitlab.com/myorg/modbus-adapter:1.4.2
pvr commit -m "Modbus 1.4.2 — fix register decode"
pvr sig add --parts=modbus
pvr post https://pvr.pantahub.com/<USER>/<DEVICE_NICK>Other containers untouched. Differential transfer ships only the changed object hashes — minutes over cellular instead of a full reflash.
Fleet-Wide Rollouts
Use Pantahub channels (see Manage an Embedded Linux Device Fleet) to stage rollouts: deploy the new Modbus container to 50 gateways, monitor error rates, then propagate to the rest of the fleet automatically.
Hardware Targets
Common gateway hardware that runs Pantavisor today:
- Raspberry Pi 3/4/5 — supported by
meta-pantavisor, pre-built images at docs.pantahub.com/initial-devices - Custom ARM64 / ARMv7 boards — via your own MACHINE definition + meta-pantavisor
- x86 industrial PCs — via meta-pantavisor with x86 BSP
See Raspberry Pi Embedded Linux with Containers for the RPi-specific quick start.
Next Steps
- Composable Firmware — Concept + workflow
- Build an Embedded Linux Image from Containers — Build the gateway image
- Manage an Embedded Linux Device Fleet — Run many gateways
- Secure OTA Updates for Embedded Linux — Sign every gateway update