This is an old revision of the document!
Table of Contents
Network Instance -- deep dive
A Network Instance defines how app workloads connect to the network. Unlike all other ZEDEDA objects, Network Instances are pushed to the device immediately on assignment – no App Instance is needed.
All content on this page has been confirmed live on device
e418565d-9d93-47ee-b7ea-aba4c9c7e912.
Network Instance types
| Type | Kind value | Description |
|---|---|---|
| Switch | NETWORK_INSTANCE_KIND_SWITCH | Pure L2 bridge – apps get IP from upstream network |
| Local | NETWORK_INSTANCE_KIND_LOCAL | NATed – EVE runs DHCP/DNS, apps get RFC1918 IPs |
| Cloud | NETWORK_INSTANCE_KIND_CLOUD | Mesh overlay – not covered here |
What triggers the push
Creating a Network Instance in Terraform or the ZEDEDA Cloud UI and assigning
it to a device or cluster is sufficient. The cloud immediately includes it in
the next DeviceConfig push to the device.
Confirmed by watching /run/zedagent/NetworkInstanceConfig/ populate within
seconds of terraform apply.
The flow
- Terraform/UI creates NI → ZEDEDA Cloud stores it
- Cloud pushes
DeviceConfigprotobuf to device (HTTPS polling) zedagentwrites/run/zedagent/NetworkInstanceConfig/<uuid>.jsonzedroutersubscribes via.socksocket and processes immediately- Host network is programmed (bridge, iptables, dnsmasq)
zedrouterpublishesNetworkInstanceStatuswithActivated: truezedrouterwrites reconciliation graphs:/run/zedrouter-intended-state.dot/run/zedrouter-current-state.dot
- Nothing in k3s yet – NAD only created when an App Instance is deployed
Confirmed log output
From /persist/newlog/collect/current.device.log immediately after NI creation:
{
"source": "zedagent",
"msg": "Network instance status modify",
"obj_uuid": "5fb60e7e-2361-4b9e-b6a4-addd62d366b0",
"diff": "ChangeInProgress: 1 -> 0, Activated: true"
}
ChangeInProgress: 1 → 0 is the reconciliation completion signal.
Activated: true confirms the NI is fully programmed and ready.
Switch type (TF-CL1-LAN1)
Terraform
resource "zedcloud_network_instance" "tf_cl1_lan_1" {
name = "TF-CL1-LAN1"
title = "TF-CL1-LAN1"
kind = "NETWORK_INSTANCE_KIND_SWITCH"
type = "NETWORK_INSTANCE_DHCP_TYPE_UNSPECIFIED"
port = "eth1"
edge_node_cluster {
id = zedcloud_edgenode_cluster.demo_edgenode_cluster_1.id
}
}
NetworkInstanceConfig JSON (confirmed live)
cat /run/zedagent/NetworkInstanceConfig/aba5cce2-8ee7-4746-a2e2-83098e49bcf6.json
| Field | Value | Meaning |
|---|---|---|
Type | 1 | Switch (L2) |
IpType | 0 | No IP config |
Subnet | null | No subnet – L2 only |
PortLabel | eth1 | Uplink NIC |
Activate | true | Active immediately |
EnableFlowlog | true | Mirror interface created |
What zedrouter creates on the host (confirmed live)
eth1itself IS the bridge (BridgeName: “eth1”) – no separate bridge createdALLOW-L2-FORWARDiptables rule:-i eth1 -o eth1 -j ACCEPT- ip6tables mirror of same rule
- Metadata HTTP server on
eth1:80at10.244.244.2 - DNAT:
169.254.169.254:80→10.244.244.2:80 physdev –physdev-in keth1blocks external metadata access- Mirror interface
eth1-mfor flow logging (8 tc-mirror rules viaketh1) - No dnsmasq, no MASQUERADE, no NAT
NetworkInstanceStatus JSON (confirmed live)
| Field | Value | Meaning |
|---|---|---|
BridgeName | “eth1” | eth1 itself is the bridge |
BridgeIPAddr | ““` | No IP on bridge |
BridgeNum | 1 | Bridge slot 1 |
MirrorIfName | “eth1-m” | Flow log mirror |
Activated | true | Fully ready |
Error | ”“` | No errors |
How to verify (Switch)
# Status cat /run/zedrouter/NetworkInstanceStatus/aba5cce2-8ee7-4746-a2e2-83098e49bcf6.json | \ jq '{Activated, BridgeName, BridgeIPAddr, MirrorIfName, Error}' # iptables rules iptables -L ALLOW-L2-FORWARD -n | grep eth1 # Mirror interface exists ip link show eth1-m # Reconciliation complete (no diff = fully reconciled) diff /run/zedrouter-intended-state.dot /run/zedrouter-current-state.dot # k3s -- no NAD yet kubectl get network-attachment-definitions -n eve-kube-app
Local type (TF-CL1-LOCAL-LAN1)
Terraform
resource "zedcloud_network_instance" "tf_cl1_lan_local_1" {
name = "TF-CL1-LOCAL-LAN1"
title = "TF-CL1-LOCAL-LAN1"
kind = "NETWORK_INSTANCE_KIND_LOCAL"
type = "NETWORK_INSTANCE_DHCP_TYPE_V4"
port = "eth0"
ip {
subnet = "10.33.0.0/24"
gateway = "10.33.0.1"
dhcp_range {
start = "10.33.0.20"
end = "10.33.0.50"
}
dns = ["1.1.1.1"]
ntp = "64.246.132.14"
}
edge_node_cluster {
id = zedcloud_edgenode_cluster.demo_edgenode_cluster_1.id
}
}
NetworkInstanceConfig JSON (confirmed live)
| Field | Value | Meaning |
|---|---|---|
Type | 2 | Local (NATed) |
IpType | 1 | IPv4 |
Subnet | 10.33.0.0/24 | App subnet |
Gateway | 10.33.0.1 | Bridge IP / default GW for apps |
DhcpRange | 10.33.0.20 - 10.33.0.50 | Pool for apps |
NtpServers | [“64.246.132.14”] | NTP for apps |
PortLabel | eth0 | Uplink NIC |
EnableFlowlog | true | Flow logging enabled |
What zedrouter creates on the host (confirmed live)
- New Linux bridge
bn2(BridgeNum: 2) with IP10.33.0.1/24 - Bridge state
DOWNuntil an app VIF is attached - dnsmasq:
/opt/zededa/bin/dnsmasq -C /run/zedrouter/dnsmasq.bn2.conf- DHCP range:
10.33.0.20 - 10.33.0.50, lease 60 min - DNS upstream:
192.168.0.1@eth0(device default GW) - NTP:
64.246.132.14 - Leases dir:
/run/zedrouter/dnsmasq.leases/bn2/ - Hosts dir:
/run/zedrouter/hosts.bn2
- MASQUERADE:
10.33.0.0/24→eth0(SNAT for outbound) - DNAT:
169.254.169.254:80→10.33.0.1:80(metadata server) - Route:
10.33.0.0/24 dev bn2(linkdown until app connects) - No mirror interface (
MirrorIfName: ”“``) – Local NI has no ''eth-m
NetworkInstanceStatus JSON (confirmed live)
| Field | Value | Meaning |
|---|---|---|
BridgeName | “bn2” | New bridge created |
BridgeIPAddr | “10.33.0.1” | Gateway IP assigned |
BridgeNum | 2 | Bridge slot 2 |
MirrorIfName | ”“` | No mirror for Local NI |
Activated | true | Fully ready |
CurrentRoutes | 0.0.0.0/0 via 192.168.0.1 | Device uplink route |
Error | ”“` | No errors |
dnsmasq config (confirmed live)
cat /run/zedrouter/dnsmasq.bn2.conf
Key confirmed settings:
interface=bn2listen-address=10.33.0.1dhcp-range=10.33.0.20,10.33.0.50,255.255.255.0,60mserver=192.168.0.1@eth0(upstream DNS)dhcp-leasefile=/run/zedrouter/dnsmasq.leases/bn2
How to verify (Local)
# Status cat /run/zedrouter/NetworkInstanceStatus/1301aff1-202b-4454-9197-2d440c24ddfd.json | \ jq '{Activated, BridgeName, BridgeIPAddr, Error}' # Bridge has IP ip addr show bn2 # dnsmasq running ps aux | grep dnsmasq # NAT rules iptables -t nat -L POSTROUTING-apps -n | grep 10.33.0 # Route ip route show | grep bn2 # Reconciliation diff /run/zedrouter-intended-state.dot /run/zedrouter-current-state.dot # k3s -- no NAD yet kubectl get network-attachment-definitions -n eve-kube-app
Switch vs Local comparison
| Feature | Switch (L2) | Local (NATed) |
|---|---|---|
| Bridge | eth1 itself | New bnX created |
| DHCP | None (upstream provides) | dnsmasq on bridge |
| NAT/MASQUERADE | None | Yes, out via uplink |
| DNS | None | dnsmasq proxies upstream |
| Mirror interface | eth1-m created | None |
| IP on bridge | None | Gateway IP (e.g. 10.33.0.1) |
| App gets IP from | Upstream L2 network | EVE dnsmasq |
| iptables rules | ALLOW-L2-FORWARD + physdev | MASQUERADE + DNAT |
| Flow logging | Via eth1-m tc-mirror | Via bridge metrics |
What appears in k3s
Nothing at NI creation time – confirmed by kubectl get network-attachment-definitions -n eve-kube-app
returning only the static bootstrap NAD:
kubectl get network-attachment-definitions -n eve-kube-app network-instance-attachment -o yaml # spec.config: '{ "cniVersion": "0.3.1", "type": "eve-bridge" }' # creationTimestamp: cluster init time -- not created by your Terraform
The per-NI NetworkAttachmentDefinition is created by zedkube only when an
App Instance referencing this NI is deployed. This has not yet been observed live
on this device – pending App Instance deployment validation.
Zedrouter state machine
zedrouter uses a declarative reconciliation model. It writes two DOT graph files:
# What zedrouter wants to achieve cat /run/zedrouter-intended-state.dot | grep -A5 "cluster_NI_" # What is currently programmed on the host cat /run/zedrouter-current-state.dot | grep -A5 "cluster_NI_" # Diff should be empty when fully reconciled diff /run/zedrouter-intended-state.dot /run/zedrouter-current-state.dot # Dependency arrows in red = unmet dependency # Dependency arrows in black = satisfied
Red arrows in the diff indicate a dependency that cannot yet be satisfied (e.g. a route depending on an interface that does not yet have an IP address). Once satisfied the arrow turns black. A clean diff means the NI is fully reconciled.
Source references
pkg/pillar/cmd/zedagent/handlenetworkinstance.go– NI config handlingpkg/pillar/cmd/zedrouter/– full zedrouter implementationpkg/pillar/types/zedroutertypes.go:1192– NetworkInstanceStatus.LogModify (confirmed in logs)
