DOCS-2997: Write the guide for connecting workloads to a VLAN - #2947
DOCS-2997: Write the guide for connecting workloads to a VLAN#2947ctauchen wants to merge 2 commits into
Conversation
✅ Deploy Preview for calico-docs-preview-next ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview succeeded!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
Adds the main “connect workloads to a VLAN” setup guide for Calico Enterprise L2 bridge networking (Calico-managed bridge), covering prerequisites, configuration steps, and verification for both KubeVirt VMs (primary path) and pods (secondary path).
Changes:
- Expands the guide with explicit prerequisites and a required “MultiInterfaceMode=Multus” configuration step.
- Documents end-to-end resource setup: IPPools (L2Workload),
Network(managed bridge + trunk + VLAN/subnets), and per-VLANNetworkAttachmentDefinition. - Adds workload attachment examples (KubeVirt VM spec + pod Multus annotations) and a structured verification checklist.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| If the only uplink on your nodes also carries the node's own IP address, $[prodname] | ||
| cannot create the bridge. Start with [Prepare an existing bridge](byo-bridge.mdx), then | ||
| return here from step 3 onwards. |
| A workload can take an L2 network as its primary interface instead of an additional one, | ||
| set with annotations rather than through Multus. See the | ||
| [Network resource](../../reference/resources/network.mdx) for the annotations. |
f5dd80b to
ddd7e3a
Compare
Fill in the main setup guide for L2 bridge networking, for nodes where Calico can take over an interface of its own. The guide leads with VMs, because the feature is aimed at them and because a VM is configured differently from a pod. The interface is declared in the VM specification, and the guide says plainly that the launcher pod is not annotated by hand. Pods follow as a secondary case. Setting the multi-interface mode gets a step of its own. Installing Multus is not sufficient, nothing reports the omission, and the symptom appears much later as a workload with two interfaces called eth0 and no connectivity. The IP pool step explains why the pool is marked as an L2 workload pool, since the consequence of leaving it out is a blackhole route and a BGP advertisement for addresses that live on the reader's own VLAN. The guide also notes that KubeVirt's bridge interface binding is unrelated to the Calico L2 bridge, because the same word means two different things in adjacent configuration. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ddd7e3a to
34d01e5
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (4)
calico-enterprise/networking/l2-bridge/connect-vlan.mdx:17
- The text says to return here “from step 3 onwards”, but the linked “Prepare an existing bridge” page currently has no numbered steps, so readers won’t know what “step 3” refers to.
If the only uplink on your nodes also carries the node's own IP address, $[prodname] cannot create the bridge.
calico-enterprise/networking/l2-bridge/connect-vlan.mdx:318
- This section sends readers to the generic Network resource reference for the primary-interface annotations, but that reference page currently documents only VRF networks and states that only
vrfis supported. That makes it a confusing reference for L2 bridge users.
A workload can take an L2 network as its primary interface instead of an additional one, set with annotations rather than through Multus.
See the [Network resource](../../reference/resources/network.mdx) for the annotations.
calico-enterprise/networking/l2-bridge/connect-vlan.mdx:32
- This sentence points readers to “L2 bridge support and limitations” for the “full set of constraints”, but
calico-enterprise/reference/l2-bridge-support.mdxis currently just an outline (headings only). As written, this over-promises content that isn’t there yet.
calico-enterprise/networking/l2-bridge/connect-vlan.mdx:97 - The guide introduces
allowedUses: [L2Workload], butcalico-enterprise/reference/resources/ippool.mdxdoes not listL2Workloadas an acceptedallowedUsesvalue (it currently lists Workload/Tunnel/HostSecondaryInterface/LoadBalancer). This mismatch is likely to confuse readers or make them think the example is invalid.
spec:
cidr: 10.10.0.0/24
allowedUses:
- L2Workload
disableBGPExport: true
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (2)
calico-enterprise/networking/l2-bridge/connect-vlan.mdx:33
- This sentence claims the linked page contains the full set of constraints, but
calico-enterprise/reference/l2-bridge-support.mdxcurrently appears to be a stub (headings only). That makes this wording misleading for readers; either populate the target page in this PR sequence or soften the claim here.
For the full set of constraints, see [L2 bridge support and limitations](../../reference/l2-bridge-support.mdx).
calico-enterprise/networking/l2-bridge/connect-vlan.mdx:318
- This section says primary-interface attachment is done "with annotations" but doesn’t name the annotation key, even though it’s already documented in
calico-enterprise/reference/resources/network.mdx. Naming the key here would make this step actionable without forcing readers to context-switch.
A workload can take an L2 network as its primary interface instead of an additional one, set with annotations rather than through Multus.
See the [Network resource](../../reference/resources/network.mdx) for the annotations.
Add a comment at the top of the page naming the user story it exists to serve. Readers on a shared uplink arrive here from the prepared-bridge guide, so this page completes that path as well as its own. That is recorded as subsidiary. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (5)
calico-enterprise/networking/l2-bridge/connect-vlan.mdx:200
- Same quoting issue here: use
nodeSelector: ""(double quotes) for consistency with other docs/examples.
interface:
name: eno2
- nodeSelector: ''
bridge:
managedBridge: {}
calico-enterprise/networking/l2-bridge/connect-vlan.mdx:329
- The link to the “Network resource” reference is likely to confuse readers in its current state:
reference/resources/network.mdxdocuments only VRF networks and explicitly says “Currently onlyvrfis supported”, but this guide usesspec.l2Bridge. Until the reference page is updated to cover L2 bridge networking, linking to it here for annotation names is misleading.
A workload can take an L2 network as its primary interface instead of an additional one, set with annotations rather than through Multus.
See the [Network resource](../../reference/resources/network.mdx) for the annotations.
calico-enterprise/networking/l2-bridge/connect-vlan.mdx:113
- The guide introduces
allowedUses: L2Workload, but the existing IPPool reference docs don’t listL2Workloadas a valid value forallowedUses(seecalico-enterprise/reference/resources/ippool.mdx, where accepted values are Workload/Tunnel/HostSecondaryInterface/LoadBalancer). Either the reference needs updating for L2 bridge networking or this example should use the documented value(s).
cidr: 10.10.0.0/24
allowedUses:
- L2Workload
disableBGPExport: true
- Apply it, and repeat for each workload VLAN.
allowedUses: L2Workload marks the pool as belonging to an L2 network.
Without it, $[prodname] treats the block as part of the routed pod network: it programs a blackhole route for it and advertises it over BGP, neither of which is correct for addresses that live on your VLAN.
**calico-enterprise/networking/l2-bridge/connect-vlan.mdx:43**
* This sentence points readers to “L2 bridge support and limitations” for the full constraints, but that page is currently just a heading skeleton (no actual constraints/limitations content). As written, this sends readers to an empty page.
- An interface on each node that $[prodname] can take over, carrying your VLANs as a trunk, and not carrying the node's own IP address.
For the full set of constraints, see L2 bridge support and limitations.
**calico-enterprise/networking/l2-bridge/connect-vlan.mdx:136**
* Use double quotes for the empty `nodeSelector` to match existing docs (for example `calico-enterprise/reference/resources/network.mdx` uses `nodeSelector: ""`).
This issue also appears on line 196 of the same file.
l2Bridge:
hostConfig:
- nodeSelector: ''
bridge:
</details>
| ::: | ||
|
|
||
| Use this guide when $[prodname] can take over an uplink of its own. If the only uplink on your nodes also carries the node's own IP address, start with [Prepare an existing bridge](byo-bridge.mdx) and return here afterwards. | ||
| Use this guide when $[prodname] can take over an interface of its own. |
There was a problem hiding this comment.
| Use this guide when $[prodname] can take over an interface of its own. | |
| Use this guide when your L2 trunk is separate from your host's primary NIC. For example, your hosts' primary NIC is `eth0` and your L2 trunk is `eth1`. |
| - [Multus](../configuring/multiple-networks.mdx) installed. | ||
| Multus is a third-party component and $[prodname] does not ship it. |
There was a problem hiding this comment.
Technically Multus is only required for secondary interfaces. Worth saying that OpenShift includes Multus by default?
There was a problem hiding this comment.
Make this conditional: If you're [correct phrase for going the managed route), you need to install Multus. OpenShift includes Multus by default.
| - [Multus](../configuring/multiple-networks.mdx) installed. | ||
| Multus is a third-party component and $[prodname] does not ship it. | ||
| - KubeVirt installed, if you are attaching VMs. | ||
| - An interface on each node that $[prodname] can take over, carrying your VLANs as a trunk, and **not** carrying the node's own IP address. |
There was a problem hiding this comment.
It's a little more than that: the interface we take over must be the one carrying your L2 trunk traffic
| - The VLAN ID. | ||
| - The subnet on that VLAN, in CIDR notation. | ||
| - The default gateway on that subnet. | ||
| - The range of addresses you are willing to let $[prodname] assign. |
There was a problem hiding this comment.
| - The range of addresses you are willing to let $[prodname] assign. | |
| - The range of addresses you are willing to let $[prodname] assign. | |
| - Any other reserved addresses on your subnet (e.g. DNS servers). |
They should create IPReservation objects to cover any reserved IPs that lie within the calico IP pools (that's existing function but it's more important for L2)
| ## Create an IP pool for each workload VLAN | ||
|
|
||
| The pool must exist before the `Network`, and its CIDR must line up with the VLAN's subnet. | ||
| The pool has to exist before the `Network`. |
There was a problem hiding this comment.
Not true, you can create them in either order (but both must exist before you try to use them).
There was a problem hiding this comment.
We can strike this statement. Claude, you've gone overboard with your pre-procedure explanation. Hard limit to two sentences. One is better.
|
|
||
| Entries are evaluated in order and **the first match wins**. | ||
| Everything after it is ignored for that node — entries are not merged. | ||
| Put your most specific selectors first and a catch-all last. |
There was a problem hiding this comment.
If you don't have a catch all at the end, some nodes won't match and they'll ignore the Network entirely, which can be useful (but it doesn't affect pod scheduling so you need to make sure Pods/VMs have nodeSelectors too if not all nodes have a bridge).
| "network": "vlan-trunk", | ||
| "vlan": 10, |
There was a problem hiding this comment.
Can you highlight these somehow; the rest is the standard NAD boilerplate
There was a problem hiding this comment.
@fasaxc Yes we can highlight. Which lines?
Claude if you revise before we have response, highlight what seems relevant and we'll adjust after.
| The `bridge: {}` here is KubeVirt's own interface binding, which attaches the VM to the interface $[prodname] built. | ||
| It is unrelated to the $[prodname] L2 bridge — the two uses of the word are unfortunate but distinct. | ||
|
|
||
| To give the VM a specific address and MAC address rather than whatever the pool offers, see [Bring a VM over with its IP and MAC](vm-identity.mdx). |
There was a problem hiding this comment.
Haven't read the other one yet, but we should document both migration and just setting IP and MAC explicitly.
|
|
||
| To give the VM a specific address and MAC address rather than whatever the pool offers, see [Bring a VM over with its IP and MAC](vm-identity.mdx). | ||
|
|
||
| ### Attaching a pod instead |
There was a problem hiding this comment.
Both here and above, worth spelling out "what you get".
Above, I think you'll get a VM with one interface, plumbed to a secondary interface of the Pod containing the VM. The primary interface of the VM's pod is unused.
Here you get the primary interface on the pod network, and the secondary interface on vlan 10
There was a problem hiding this comment.
Agreed. Let's spell this out as part of the 'why' for the section. "By the end, you'll get ..."
| - vlan: { id: 10 } | ||
| subnets: | ||
| - cidr: 10.10.0.0/24 | ||
| - vlan: { id: 20 } | ||
| subnets: | ||
| - cidr: 10.20.0.0/24 |
There was a problem hiding this comment.
Include the gateway routes here, will almost certainly want those.
There was a problem hiding this comment.
Include these and ask if examples needed.
| - [Multus](../configuring/multiple-networks.mdx) installed. | ||
| Multus is a third-party component and $[prodname] does not ship it. |
There was a problem hiding this comment.
Make this conditional: If you're [correct phrase for going the managed route), you need to install Multus. OpenShift includes Multus by default.
| - An active $[prodname] license. | ||
| - [Multus](../configuring/multiple-networks.mdx) installed. | ||
| Multus is a third-party component and $[prodname] does not ship it. | ||
| - KubeVirt installed, if you are attaching VMs. |
There was a problem hiding this comment.
Lead with conditional.
| ## Create an IP pool for each workload VLAN | ||
|
|
||
| The pool must exist before the `Network`, and its CIDR must line up with the VLAN's subnet. | ||
| The pool has to exist before the `Network`. |
There was a problem hiding this comment.
We can strike this statement. Claude, you've gone overboard with your pre-procedure explanation. Hard limit to two sentences. One is better.
| - vlan: { id: 10 } | ||
| subnets: | ||
| - cidr: 10.10.0.0/24 | ||
| - vlan: { id: 20 } | ||
| subnets: | ||
| - cidr: 10.20.0.0/24 |
There was a problem hiding this comment.
Include these and ask if examples needed.
| "network": "vlan-trunk", | ||
| "vlan": 10, |
There was a problem hiding this comment.
@fasaxc Yes we can highlight. Which lines?
Claude if you revise before we have response, highlight what seems relevant and we'll adjust after.
|
|
||
| To give the VM a specific address and MAC address rather than whatever the pool offers, see [Bring a VM over with its IP and MAC](vm-identity.mdx). | ||
|
|
||
| ### Attaching a pod instead |
There was a problem hiding this comment.
Agreed. Let's spell this out as part of the 'why' for the section. "By the end, you'll get ..."

Fills in the main setup guide for L2 bridge networking. Fifth in the DOCS-2997 sequence. It follows the guide for preparing an existing bridge, because a reader on a shared uplink needs that first.
This is the path for nodes where Calico can take over an interface of its own, so Calico creates and owns the bridge.
The guide leads with VMs. The feature is aimed at them, and a VM is configured differently from a pod: the interface is declared in the VM specification, and the guide says plainly that the launcher pod KubeVirt creates is not annotated by hand. Pods follow as the secondary case.
Points worth a reviewer's attention:
Three things I could not verify and would like checked:
Changed page: https://deploy-preview-2947--calico-docs-preview-next.netlify.app/calico-enterprise/next/networking/l2-bridge/connect-vlan