Skip to content

DOCS-2997: Write the guide for connecting workloads to a VLAN - #2947

Open
ctauchen wants to merge 2 commits into
tigera:mainfrom
ctauchen:DOCS-2997-5-connect-vlan
Open

DOCS-2997: Write the guide for connecting workloads to a VLAN#2947
ctauchen wants to merge 2 commits into
tigera:mainfrom
ctauchen:DOCS-2997-5-connect-vlan

Conversation

@ctauchen

@ctauchen ctauchen commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

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:

  • Setting the multi-interface mode gets a step of its own. Installing Multus is not sufficient, nothing reports the omission, and the symptom shows up 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. Leaving it out gives you a blackhole route and a BGP advertisement for addresses that live on your own VLAN.
  • The first-match-wins rule for host configuration entries is stated explicitly, since nothing in the API hints at it.
  • The guide notes that KubeVirt's bridge interface binding is unrelated to the Calico L2 bridge. The same word means two different things in adjacent configuration and readers will trip on it.

Three things I could not verify and would like checked:

  • The VM specification example is written from KubeVirt's documented API rather than copied from a working VM. Alex has manifests from the EP2 hash release that would be better.
  • The attachment definition sets vlan as a JSON number. Confirm that is the expected type.
  • The section on using an L2 network as the primary interface describes the capability and its consequence, but points at the Network reference for the annotation names rather than naming them, because I could not confirm the exact keys. That reference page is still to be written, so the pointer needs filling in.

Changed page: https://deploy-preview-2947--calico-docs-preview-next.netlify.app/calico-enterprise/next/networking/l2-bridge/connect-vlan

Copilot AI lite review requested due to automatic review settings August 21, 2026 10:52
@ctauchen
ctauchen requested a review from a team as a code owner August 21, 2026 10:52
@netlify

netlify Bot commented Aug 21, 2026

Copy link
Copy Markdown

Deploy Preview for calico-docs-preview-next ready!

Name Link
🔨 Latest commit e4a1261
🔍 Latest deploy log https://app.netlify.com/projects/calico-docs-preview-next/deploys/6a88577d98e5cb0008f16690
😎 Deploy Preview https://deploy-preview-2947--calico-docs-preview-next.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Aug 21, 2026

Copy link
Copy Markdown

Deploy Preview succeeded!

Built without sensitive environment variables

Name Link
🔨 Latest commit e4a1261
🔍 Latest deploy log https://app.netlify.com/projects/tigera/deploys/6a88577d471fef0008df3b10
😎 Deploy Preview https://deploy-preview-2947--tigera.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 57 (🔴 down 17 from production)
Accessibility: 98 (no change from production)
Best Practices: 92 (no change from production)
SEO: 100 (no change from production)
PWA: -
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-VLAN NetworkAttachmentDefinition.
  • 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.

Comment on lines +16 to +18
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.
Comment on lines +331 to +333
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.
Copilot AI review requested due to automatic review settings August 21, 2026 11:08
@ctauchen
ctauchen force-pushed the DOCS-2997-5-connect-vlan branch from f5dd80b to ddd7e3a Compare August 21, 2026 11:08
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>
@ctauchen
ctauchen force-pushed the DOCS-2997-5-connect-vlan branch from ddd7e3a to 34d01e5 Compare August 21, 2026 11:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 vrf is 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.mdx is 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], but calico-enterprise/reference/resources/ippool.mdx does not list L2Workload as an accepted allowedUses value (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

Copilot AI review requested due to automatic review settings August 21, 2026 11:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.mdx currently 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>
Copilot AI review requested due to automatic review settings August 21, 2026 13:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.mdx documents only VRF networks and explicitly says “Currently only vrf is supported”, but this guide uses spec.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 list L2Workload as a valid value for allowedUses (see calico-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
  1. 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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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`.

Comment on lines +28 to +29
- [Multus](../configuring/multiple-networks.mdx) installed.
Multus is a third-party component and $[prodname] does not ship it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically Multus is only required for secondary interfaces. Worth saying that OpenShift includes Multus by default?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- 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`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not true, you can create them in either order (but both must exist before you try to use them).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Comment on lines +217 to +218
"network": "vlan-trunk",
"vlan": 10,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you highlight these somehow; the rest is the standard NAD boilerplate

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Let's spell this out as part of the 'why' for the section. "By the end, you'll get ..."

Comment on lines +133 to +138
- vlan: { id: 10 }
subnets:
- cidr: 10.10.0.0/24
- vlan: { id: 20 }
subnets:
- cidr: 10.20.0.0/24

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include the gateway routes here, will almost certainly want those.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include these and ask if examples needed.

Comment on lines +28 to +29
- [Multus](../configuring/multiple-networks.mdx) installed.
Multus is a third-party component and $[prodname] does not ship it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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`.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can strike this statement. Claude, you've gone overboard with your pre-procedure explanation. Hard limit to two sentences. One is better.

Comment on lines +133 to +138
- vlan: { id: 10 }
subnets:
- cidr: 10.10.0.0/24
- vlan: { id: 20 }
subnets:
- cidr: 10.20.0.0/24

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include these and ask if examples needed.

Comment on lines +217 to +218
"network": "vlan-trunk",
"vlan": 10,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Let's spell this out as part of the 'why' for the section. "By the end, you'll get ..."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants