# OVN Deep-Dive: Incus Cluster Networking Internals
How Incus translates its network abstractions into OVN logical objects,
OVS physical flows, and Geneve tunnels. Based on a live 3-node IncusOS
cluster running OVS 3.6.1 with an OVN overlay network.
## Lab Topology
```mermaid
flowchart TD
lan(("LAN
192.168.100.0/22"))
n1["oc-node-01 · .140
Geneve endpoint"]
n2["oc-node-02 · .141
Geneve endpoint"]
n3["oc-node-03 · .142
Geneve + gateway"]
uplink("UPLINK
OVN range .103.200-210")
netprod("net-prod
10.10.10.0/24")
lan --- n1 & n2 & n3
lan --- uplink --> netprod
classDef node fill:#009E73,color:#fff,stroke:#007a5e
classDef network fill:#0072B2,color:#fff,stroke:#005a8e
class n1,n2,n3 node
class lan,uplink,netprod network
```
**Instances on net-prod:**
| Instance | IP | Node | Role |
|---|---|---|---|
| ffsdn-haproxy-52-01 | 10.10.10.50 | oc-node-01 | HAProxy LB |
| ffsdn-haproxy-52-02 | 10.10.10.51 | oc-node-02 | HAProxy LB |
| nginx-lb-01 | 10.10.10.60 | oc-node-01 | Nginx backend |
| nginx-lb-02 | 10.10.10.61 | oc-node-02 | Nginx backend |
| nginx-lb-03 | 10.10.10.62 | oc-node-03 | Nginx backend |
| test-app-manual-web-tier-app-1 | 10.10.10.4 | oc-node-03 | App server |
| test-app-manual-web-tier-web-1 | 10.10.10.2 | oc-node-01 | Web server |
| test-app-manual-web-tier-web-2 | 10.10.10.3 | oc-node-02 | Web server |
## OVN Northbound Database (Logical Layer)
The NB database describes the desired network topology. Incus creates and
manages all objects here — users never touch OVN directly.
### Incus → OVN Naming Convention
Incus uses a consistent naming scheme. For a network named `net-prod`
with internal ID `net8`:
| Incus Concept | OVN Object | OVN Name |
|---|---|---|
| OVN network | Logical Switch (internal) | `incus-net8-ls-int` |
| OVN uplink | Logical Switch (external) | `incus-net8-ls-ext` |
| OVN gateway | Logical Router | `incus-net8-lr` |
| Instance NIC | Logical Switch Port | `incus-net8-instance--eth0` |
| Router↔int switch | Router Port + Switch Port | `incus-net8-lr-lrp-int` / `incus-net8-ls-int-lsp-router` |
| Router↔ext switch | Router Port + Switch Port | `incus-net8-lr-lrp-ext` / `incus-net8-ls-ext-lsp-router` |
| Provider connection | Localnet Port | `incus-net8-ls-ext-lsp-provider` |
| Network forward LB | Load Balancer | `incus-net8-lb--` |
The numeric `net8` suffix is an internal Incus identifier for the network.
It increments as networks are created.
### Logical Switches
**Internal switch** (`incus-net8-ls-int`): The overlay network where all
instances connect. Subnet 10.10.10.0/24, IPv6 fd42:842b:1e5f:b27::/64.
```
incus-net8-ls-int
├── 8 instance ports (one per container on net-prod)
├── 1 router port (incus-net8-ls-int-lsp-router)
├── 1 load balancer attached (VIP 192.168.103.200:80)
├── 15 ACL rules (Incus baseline security)
├── 8 DNS records (forward + reverse per instance)
└── DHCP options (IPv4 lease=3600, MTU=1442, DNS=192.168.100.1)
```
Key config in `other_config`:
- `subnet=10.10.10.0/24` — Incus IPAM range
- `exclude_ips=10.10.10.1 10.10.10.254 ...` — reserved IPs (gateway, broadcast, static assignments)
- `ipv6_prefix=fd42:842b:1e5f:b27::/64` — SLAAC prefix
**External switch** (`incus-net8-ls-ext`): Bridges the logical router to
the physical UPLINK network.
```
incus-net8-ls-ext
├── 1 localnet port (incus-net8-ls-ext-lsp-provider)
│ └── options: network_name=UPLINK
└── 1 router port (incus-net8-ls-ext-lsp-router)
└── nat_addresses: 10:66:6a:d4:30:c7 192.168.103.200
is_chassis_resident("cr-incus-net8-lr-lrp-ext")
```
The `localnet` port type is special — it tells OVN to bridge this logical
switch to a physical network via `ovn-bridge-mappings` on each chassis.
The mapping `UPLINK:incusovn7` connects it to the OVS provider bridge.
### Logical Router
**Router** (`incus-net8-lr`): Connects internal overlay to external UPLINK.
```
incus-net8-lr
├── lrp-int: 10.10.10.1/24, fd42:842b:1e5f:b27::1/64
│ MAC: 10:66:6a:d4:30:c7, MTU: 1442
│ IPv6 RA: periodic, DHCPv6 stateless, DNSSL=incus
│
├── lrp-ext: 192.168.103.200/22
│ MAC: 10:66:6a:d4:30:c7, MTU: 1442
│ HA Chassis Group: incus-net8 (gateway on oc-node-03)
│
├── NAT:
│ └── SNAT: 10.10.10.0/24 → 192.168.103.200 (stateful)
│
├── Static Routes:
│ ├── 0.0.0.0/0 → 192.168.100.1 via lrp-ext (default gateway)
│ └── 192.168.103.200/32 → 10.10.10.1 (hairpin VIP route)
│
├── Load Balancer: (same LB as on ls-int)
│ └── 192.168.103.200:80 → 10.10.10.50:80, 10.10.10.51:80
│
├── Policies:
│ ├── priority 600: allow ip4.src == $incus_net8_routes_ip4
│ ├── priority 600: allow ip6.src == $incus_net8_routes_ip6
│ └── priority 500: drop (inport == lrp-int) ← deny-by-default
│
└── Options:
├── always_learn_from_arp_request=false
└── dynamic_neigh_routers=true
```
**Router policies explained:** The priority-500 drop rule blocks all
traffic from the internal network by default. The priority-600 allow rules
create exceptions for the network's own IP ranges. This prevents instances
from spoofing source IPs outside their assigned ranges.
**Hairpin VIP route:** The `192.168.103.200/32 → 10.10.10.1` route
handles the case where an instance on net-prod accesses the VIP from
inside the overlay. Without this, the router would try to route the VIP
out the external port, but the packet originated internally.
### Load Balancer
```
Name: incus-net8-lb-192.168.103.200-tcp
Protocol: tcp
VIPs: 192.168.103.200:80 → 10.10.10.50:80, 10.10.10.51:80
```
Incus creates this LB from `incus network forward` or from Aether's
HAProxy deployment. The LB is attached to both the logical switch
(ls-int) and the logical router (lr). This dual attachment ensures
the LB intercepts traffic regardless of where it enters:
- **On the router**: Catches external traffic (LAN → VIP)
- **On the switch**: Catches internal traffic (instance → VIP)
### DHCP and DNS
**DHCP Options** (IPv4):
- Server: 10.10.10.1, MAC: 10:66:6a:d4:30:c7
- Lease time: 3600s, MTU: 1442 (Geneve overhead: 1500 - 58 = 1442)
- DNS: 192.168.100.1 (upstream resolver from UPLINK config)
- Domain: incus, search list: incus
**DHCP Options** (IPv6):
- Stateless DHCPv6 (SLAAC for addresses, DHCPv6 for DNS)
- DNS: fd42:842b:1e5f:b27::1
**DNS Records**: Incus creates forward and reverse DNS entries for every
instance. The DNS server runs inside the OVN router (lrp-int port).
```
ffsdn-haproxy-52-01.incus → 10.10.10.50, fd42:842b:1e5f:b27:1266:6aff:fe67:3482
ffsdn-haproxy-52-02.incus → 10.10.10.51, fd42:842b:1e5f:b27:1266:6aff:fe5f:418f
nginx-lb-01.incus → 10.10.10.60, fd42:842b:1e5f:b27:1266:6aff:fe2a:da10
nginx-lb-02.incus → 10.10.10.61, fd42:842b:1e5f:b27:1266:6aff:fe69:888e
nginx-lb-03.incus → 10.10.10.62, fd42:842b:1e5f:b27:1266:6aff:fe90:4ab8
```
### ACLs (Baseline Security)
Incus creates a default ACL set on the internal switch. These are not
user-configurable — they form the baseline security policy:
| Priority | Direction | Match | Action | Purpose |
|---|---|---|---|---|
| 200 | to-lport | `arp \|\| nd` | allow | ARP/ND always allowed |
| 200 | to-lport | `icmp4.type == {3,11,12} && ip.ttl == 255` | allow | ICMP errors |
| 200 | to-lport | `igmp && ip.ttl == 1 && ip4.mcast` | allow | IGMP |
| 200 | to-lport | Router port ping echo reply | allow | Router ping |
| 200 | to-lport | DHCP to router port | allow | DHCP relay |
| 200 | to-lport | DNS to router port | allow | DNS queries |
| 200 | to-lport | `nd_ra` from router | allow | IPv6 RA |
| 200 | to-lport | `nd_rs` to router | allow | IPv6 RS |
| 200 | to-lport | `tcp.flags == 0x014` | allow | TCP RST+ACK |
All ACLs are `to-lport` (ingress to the logical port), which means they
filter traffic arriving at a port. There are no explicit `from-lport`
rules — the default for egress is allow.
## OVN Southbound Database (Physical Layer)
The SB database maps logical topology to physical infrastructure. It's
computed by `ovn-northd` from the NB database and consumed by
`ovn-controller` on each chassis.
### Chassis
Each Incus cluster member registers as a chassis:
| Chassis | Hostname | Geneve IP | OVS UUID |
|---|---|---|---|
| b840b5b2-... | oc-node-01 | 192.168.102.140 | 298b76a9-... |
| 4652b51f-... | oc-node-02 | 192.168.102.141 | 5ee7906c-... |
| 3f7400f9-... | oc-node-03 | 192.168.102.142 | fd0c3d36-... |
All chassis use Geneve encapsulation with checksum enabled (`csum=true`).
Each chassis advertises `ovn-bridge-mappings=UPLINK:incusovn7`.
Key `other_config` values:
- `datapath-type=system` — kernel datapath (not DPDK)
- `ovn-bridge-mappings=UPLINK:incusovn7` — maps logical "UPLINK" network to OVS bridge
- `ovn-monitor-all=false` — each chassis only gets flows relevant to its local ports
- `ct-commit-nat-v2=true`, `ct-next-zone=true` — modern conntrack features
### Datapath Bindings (Tunnel Keys)
Each logical switch/router gets a unique tunnel key used inside Geneve:
| Tunnel Key | Datapath | OVN Object |
|---|---|---|
| 1 | incus-net8-lr | Logical Router |
| 2 | incus-net8-ls-ext | External Switch |
| 3 | incus-net8-ls-int | Internal Switch |
When a packet traverses a Geneve tunnel, the tunnel key (VNI) identifies
which datapath it belongs to.
### Port Bindings
Port bindings map logical ports to physical chassis:
| Logical Port | Type | Chassis | Tunnel Key |
|---|---|---|---|
| Instance ports (8 total) | VIF | Respective chassis | 2-10 |
| cr-incus-net8-lr-lrp-ext | chassisredirect | oc-node-03 | 2 |
| incus-net8-lr-lrp-ext | patch | (distributed) | 1 |
| incus-net8-lr-lrp-int | patch | (distributed) | 3 |
| incus-net8-ls-ext-lsp-provider | localnet | (all chassis) | 2 |
| incus-net8-ls-ext-lsp-router | patch | (distributed) | 1 |
**Key port types:**
- **VIF (Virtual Interface)**: Regular instance ports, bound to a specific
chassis. Each has a MAC+IP pair for the instance.
- **patch**: Connects two OVN datapaths (switch↔router). Distributed —
processed locally on every chassis.
- **chassisredirect**: The gateway port. Centralizes external-facing
traffic on a single chassis for SNAT/DNAT. Bound to oc-node-03.
- **localnet**: Maps to a physical network. Present on every chassis
via `ovn-bridge-mappings`.
### HA Chassis Group (Gateway Failover)
The gateway port uses an HA chassis group for failover:
```
HA Chassis Group: incus-net8
├── oc-node-03 priority 22372 ← active gateway
├── oc-node-02 priority 18011 ← first failover
└── oc-node-01 priority 12213 ← second failover
```
Incus assigns random-looking priorities (likely based on a hash). If
oc-node-03 goes down, the `cr-incus-net8-lr-lrp-ext` binding migrates
to oc-node-02, then oc-node-01 if needed. BFD (Bidirectional Forwarding
Detection) between chassis enables fast failure detection.
### Logical Flows (Compiled Pipeline)
The lflow pipeline is the compiled form of all NB rules. For ls-int ingress:
| Table | Name | Key Rules |
|---|---|---|
| 0 | ls_in_check_port_sec | Drop multicast src, VLAN tagged |
| 1 | ls_in_apply_port_sec | Enforce port security |
| 4 | ls_in_pre_acl | Skip ACL for router port traffic |
| 5 | ls_in_pre_lb | Pre-process for LB (set reg0[2] for CT) |
| 6 | ls_in_pre_stateful | **LB intercept**: dst=192.168.103.200:80 → `ct_lb_mark` |
| 7 | ls_in_acl_hint | Compute ACL hints from conntrack state |
| 8 | ls_in_acl | Apply ACLs, track connections |
The critical LB flow in table 6:
```
priority=120, match=(reg0[2] == 1 && ip4.dst == 192.168.103.200 && tcp.dst == 80)
action=(reg1 = 192.168.103.200; reg2[0..15] = 80; ct_lb_mark;)
```
This intercepts packets to the VIP and sends them through conntrack
load balancing, which selects a backend and rewrites the destination.
## OVS Physical Layer
Each IncusOS node runs OVS 3.6.1 with two bridges.
### Bridge Architecture
```mermaid
flowchart LR
subgraph provider["incusovn7 · provider bridge"]
nic["physical NIC"]
intport["internal port"]
patch1["patch to br-int"]
end
subgraph integration["br-int · integration bridge"]
veth["instance veth ports"]
ovntun["Geneve tunnels"]
patch2["patch to incusovn7"]
end
patch1 <-->|"patch port"| patch2
classDef prov fill:#0072B2,color:#fff,stroke:#005a8e
classDef integ fill:#009E73,color:#fff,stroke:#007a5e
class nic,intport,patch1 prov
class veth,ovntun,patch2 integ
style provider fill:#e0eef8,stroke:#0072B2
style integration fill:#e6f5f0,stroke:#009E73
```
**`br-int` (integration bridge)**:
- `fail_mode: secure` — drops all traffic if OVN controller disconnects
- Contains all instance veth ports, Geneve tunnel ports, and the patch port
to the provider bridge
- All OVN logical processing (ACLs, NAT, LB, routing) happens here via
OpenFlow rules installed by `ovn-controller`
**`incusovn7` (provider bridge)**:
- Named after the Incus-managed OVS bridge (`incusovn` + network ID)
- Contains the physical NIC port, an internal port, and a patch port to br-int
- Single OpenFlow rule: `priority=0 actions=NORMAL` (standard L2 switching)
- This bridge is the on-ramp/off-ramp between OVN and the physical network
### Geneve Tunnels (Full Mesh)
Every pair of chassis has a Geneve tunnel with BFD health monitoring:
```mermaid
graph LR
n1(("oc-node-01
.140"))
n2(("oc-node-02
.141"))
n3(("oc-node-03
.142"))
n1 <-->|"Geneve 6081"| n2
n2 <-->|"Geneve 6081"| n3
n1 <-->|"Geneve 6081"| n3
classDef chassis fill:#009E73,color:#fff,stroke:#007a5e
class n1,n2,n3 chassis
```
| Source | Destination | OVS Port Name | BFD State |
|---|---|---|---|
| .140 (node-01) | .141 (node-02) | ovn-4652b5-0 | forwarding=true |
| .140 (node-01) | .142 (node-03) | ovn-3f7400-0 | forwarding=true |
| .141 (node-02) | .140 (node-01) | ovn-b840b5-0 | forwarding=true |
| .141 (node-02) | .142 (node-03) | ovn-3f7400-0 | forwarding=true |
| .142 (node-03) | .140 (node-01) | ovn-b840b5-0 | forwarding=true |
| .142 (node-03) | .141 (node-02) | ovn-4652b5-0 | forwarding=true |
Tunnel port names use the first 6 hex chars of the remote chassis name.
Options: `key=flow` (tunnel key set per-packet from OVN datapath), `csum=true`.
### Veth Port Mapping
Each instance on net-prod gets a veth pair: one end in the container's
network namespace, the other plugged into br-int. The OVS `external_ids`
field links the veth to its OVN logical port.
**oc-node-01:**
| veth | OVN Port (iface-id) | Instance | OVS ofport |
|---|---|---|---|
| veth8bd9abf3 | ...c446bd6a...-eth0 | nginx-lb-01 (10.10.10.60) | 8 |
| vethfc99b1ec | ...b90abddd...-eth0 | test-web-tier-web-1 (10.10.10.2) | 5 |
| veth07cf17cf | ...0880f911...-eth0 | ffsdn-haproxy-52-01 (10.10.10.50) | 9 |
**oc-node-02:**
| veth | OVN Port (iface-id) | Instance |
|---|---|---|
| veth3352cb4f | ...a2cad635...-eth0 | ffsdn-haproxy-52-02 (10.10.10.51) |
| vethc0b4d30a | ...07270515...-eth0 | nginx-lb-02 (10.10.10.61) |
| vethd2307cca | ...b2cbf869...-eth0 | test-web-tier-web-2 (10.10.10.3) |
**oc-node-03:**
| veth | OVN Port (iface-id) | Instance |
|---|---|---|
| veth9306f6ef | ...292ce9ce...-eth0 | nginx-lb-03 (10.10.10.62) |
| veth4701597a | ...1f056d1a...-eth0 | test-web-tier-app-1 (10.10.10.4) |
## Packet Trace: LAN → VIP → HAProxy → Nginx → Return
Complete path for an HTTP request from a LAN client to
`http://192.168.103.200/` (the HAProxy VIP).
### 1. Client → Gateway Chassis
```
Client (192.168.1.x) sends TCP SYN to 192.168.103.200:80
→ LAN switch forwards to oc-node-03 (gateway chassis)
192.168.103.200 is announced by oc-node-03 via the
cr-incus-net8-lr-lrp-ext chassisredirect port
→ Packet enters physical NIC → incusovn7 bridge
→ OVS NORMAL action → patch port → br-int
```
Why oc-node-03? The external router port (`lrp-ext`) uses HA chassis
scheduling, and oc-node-03 has the highest priority (22372). OVN makes
oc-node-03's OVS respond to ARP for 192.168.103.200, directing all
external traffic to this node.
### 2. br-int → OVN Router (External Processing)
```
br-int receives packet on patch port
→ OpenFlow table 0: classify as localnet traffic (metadata=0x2, ls-ext)
→ Pipeline: ls-ext ingress → router pipeline
→ Router receives on lrp-ext (192.168.103.200/22)
```
### 3. OVN Load Balancer DNAT
```
Router detects dst=192.168.103.200:80 matches LB
→ ct_lb_mark: conntrack creates new entry
→ DNAT: rewrite dst to 10.10.10.50:80 or 10.10.10.51:80
(round-robin selection, conntrack-aware)
→ Packet now has dst=10.10.10.50:80 (say, haproxy-01)
```
The LB is processed in the router pipeline because it's attached to both
the router and the internal switch. For external traffic, the router
processes it first.
### 4. Router → Internal Switch
```
Router forwards via lrp-int (10.10.10.1)
→ Enters ls-int pipeline
→ ACL check (baseline allows established connections)
→ Destination lookup: 10.10.10.50 → ffsdn-haproxy-52-01
→ Port binding: haproxy-01 is on oc-node-01
```
### 5. Geneve Tunnel (Cross-Chassis)
Since the gateway is on oc-node-03 but the destination (haproxy-01) is
on oc-node-01:
```
br-int on oc-node-03:
→ Output action: tunnel to oc-node-01
→ Geneve encapsulate:
- Outer: src=192.168.102.142, dst=192.168.102.140, UDP:6081
- VNI (tunnel key): 3 (ls-int datapath)
- TUN_METADATA0: encodes reg14 (destination port) + reg15
→ Physical NIC sends to LAN
oc-node-01 receives Geneve packet:
→ br-int decapsulates
→ Restores metadata from tunnel headers
→ Delivers to veth07cf17cf (haproxy-01's veth, ofport 9)
→ Packet enters container's network namespace
```
### 6. HAProxy Processing
```
HAProxy receives HTTP request on 10.10.10.50:80
→ HAProxy selects backend: nginx-lb-01 (10.10.10.60)
→ Opens new TCP connection to 10.10.10.60:80
→ Proxies request
```
### 7. HAProxy → Nginx (Same or Different Chassis)
If nginx-lb-01 is on the same node (oc-node-01):
```
→ br-int local delivery (no tunnel needed)
→ veth8bd9abf3 (nginx-lb-01, ofport 8)
```
If HAProxy chose nginx-lb-02 (oc-node-02) or nginx-lb-03 (oc-node-03):
```
→ Geneve tunnel to remote node
→ Same encap/decap as step 5
```
### 8. Return Path
```
Nginx response → HAProxy (reverse of step 7)
→ HAProxy response → OVN (enters ls-int as src=10.10.10.50)
→ ls-int → router (dst is LAN client, matches default route)
→ Router SNAT: src 10.10.10.50 → 192.168.103.200
→ lrp-ext → ls-ext → provider bridge → physical NIC → LAN
→ Client receives response from 192.168.103.200
```
**Important**: The return path from HAProxy to the LAN client goes through
the gateway chassis (oc-node-03) because SNAT is centralized there. If
HAProxy is on oc-node-01, the reply tunnels to oc-node-03 for SNAT, then
exits to the LAN.
## MTU: Why 1442?
Standard Ethernet MTU is 1500 bytes. Geneve adds 58 bytes of overhead:
```
Outer Ethernet: 14 bytes
Outer IP: 20 bytes
Outer UDP: 8 bytes
Geneve header: 16 bytes (8 base + 8 metadata)
─────────
Total overhead: 58 bytes
Inner MTU: 1442 bytes (1500 - 58)
```
Incus sets `bridge.mtu=1442` on net-prod and propagates this via:
- DHCP option: `mtu=1442`
- Router port option: `gateway_mtu=1442`
- IPv6 RA: `mtu=1442`
## Inspection Tools
### Using ovn-inspect
The `incusos/helpers/ovn-inspect` script provides structured inspection:
```bash
# NB database (logical topology)
incusos/helpers/ovn-inspect --nb
# SB database (physical bindings)
incusos/helpers/ovn-inspect --sb
# OVS on each node (deploys temp containers, cleans up after)
incusos/helpers/ovn-inspect --ovs
# Everything
incusos/helpers/ovn-inspect --full
# Trace a VIP's packet path
incusos/helpers/ovn-inspect --trace 192.168.103.200
# Dry run (show commands without executing)
incusos/helpers/ovn-inspect --nb --dry-run
```
### Manual Commands
All NB/SB commands go through the `ovn-central` container:
```bash
# NB: Logical topology
incus exec oc-node-01:ovn-central -- ovn-nbctl ls-list
incus exec oc-node-01:ovn-central -- ovn-nbctl lr-list
incus exec oc-node-01:ovn-central -- ovn-nbctl lsp-list incus-net8-ls-int
incus exec oc-node-01:ovn-central -- ovn-nbctl lr-nat-list incus-net8-lr
incus exec oc-node-01:ovn-central -- ovn-nbctl lb-list
incus exec oc-node-01:ovn-central -- ovn-nbctl acl-list incus-net8-ls-int
# SB: Physical bindings
incus exec oc-node-01:ovn-central -- ovn-sbctl show
incus exec oc-node-01:ovn-central -- ovn-sbctl list Chassis
incus exec oc-node-01:ovn-central -- ovn-sbctl list Port_Binding
incus exec oc-node-01:ovn-central -- ovn-sbctl lflow-list incus-net8-ls-int
# OVS: Physical layer (requires privileged container with /run/openvswitch)
ovs-vsctl show
ovs-ofctl dump-flows br-int
ovs-vsctl get Interface external_ids
```
### Incus CLI Cross-Reference
```bash
# See OVN network config
incus network show oc-node-01:net-prod --target oc-node-01
# See UPLINK config
incus network show oc-node-01:UPLINK --target oc-node-01
# See which instances use net-prod
incus list oc-node-01: -f csv -c n4l | grep "10.10.10"
# Network forwards (LB VIPs managed via Incus)
incus network forward list oc-node-01:net-prod
```
## Key Architectural Insights
1. **Incus fully manages OVN**: Users never interact with OVN directly.
`incus network create`, `incus network forward`, and instance NIC
configs translate to OVN objects automatically.
2. **Single gateway chassis**: All external traffic (SNAT, DNAT, LB for
external VIPs) is centralized on one node. This is a potential
bottleneck but simplifies state management. HA failover handles node
failures.
3. **Distributed routing for internal traffic**: East-west traffic
between instances on the same switch is fully distributed. No traffic
goes through the gateway unless it needs SNAT/DNAT.
4. **LB is in OVN, not HAProxy**: The VIP load balancing between HAProxy
instances is done by OVN's built-in L4 LB (conntrack-based). HAProxy
then does L7 load balancing to nginx backends. This is a two-tier LB
architecture.
5. **BFD for fast failover**: All Geneve tunnels have BFD enabled, which
detects chassis failures in ~3×detection-interval (typically <1s),
much faster than relying on OVN cluster heartbeats.
6. **MTU must be consistent**: The 1442 byte MTU is critical. If any
path (physical switch, hypervisor NIC) has MTU < 1500, Geneve
encapsulated packets will be fragmented or dropped.