<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tigera Inc</title>
    <description>The latest articles on DEV Community by Tigera Inc (tigeraio).</description>
    <link>https://dev.to/tigeraio</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F12572%2Fe692e88e-7a1e-49d5-870b-930d459570c0.png</url>
      <title>DEV Community: Tigera Inc</title>
      <link>https://dev.to/tigeraio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tigeraio"/>
    <language>en</language>
    <item>
      <title>You’ve Been Running a Kubernetes Security Model in NSX and Didn’t Even Know It</title>
      <dc:creator>Alister Baroi</dc:creator>
      <pubDate>Mon, 10 Aug 2026 19:19:22 +0000</pubDate>
      <link>https://dev.to/tigeraio/youve-been-running-a-kubernetes-security-model-in-nsx-and-didnt-even-know-it-3l47</link>
      <guid>https://dev.to/tigeraio/youve-been-running-a-kubernetes-security-model-in-nsx-and-didnt-even-know-it-3l47</guid>
      <description>&lt;p&gt;One of the blockers to moving VMs off vSphere and onto Kubernetes is losing NSX and the protection it provides. Security teams that have spent years building out distributed firewall policy look at Kubernetes and are, quite understandably, alarmed by the flat network and the fact that any workload can reach any other by default.&lt;/p&gt;

&lt;p&gt;How will they enforce east-west traffic controls? Will they be able to replicate &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/cloud-director/vmware-cloud-director/10-5/-nsxp-10-5/distributed-firewall-service.html" rel="noopener noreferrer"&gt;NSX distributed firewall&lt;/a&gt; rules with the same granularity? What about security groups, tiered policy, and rules that travel with the workload when it moves? These are important questions that must be answered before migration can begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrate vSphere to Kubernetes with microsegmentation intact
&lt;/h2&gt;

&lt;p&gt;Calico addresses vSphere to Kubernetes security concerns with a &lt;a href="https://www.tigera.io/learn/guides/microsegmentation/" rel="noopener noreferrer"&gt;network policy model&lt;/a&gt; that maps directly to key features of the NSX distributed firewall (NSX DFW). Every property NSX DFW users rely on has a direct Calico equivalent: tiered governance, workload-identity enforcement, distributed kernel-level inspection, and dynamic workload grouping. Teams coming from vSphere will recognise the pattern quickly.&lt;/p&gt;

&lt;p&gt;Let’s walk through each one in detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Distributed Enforcement
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq9ogy5iigy3hdblw52r4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq9ogy5iigy3hdblw52r4.png" alt="Enforcement on the host means better performance and automatic scaling" width="799" height="580"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Enforcement on the host means better performance and automatic scaling&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Traditional firewalls sit at the edge of the network. Traffic between workloads inside the data center has to travel out to that inspection point and back, even if the source and destination are on the same physical host. The NSX distributed firewall takes a different approach: the DFW runs in the kernel of every ESXi host and enforces policy directly at each VM’s network interface, before traffic leaves the VM.&lt;/p&gt;

&lt;p&gt;The practical effect is that inspection happens at the source. A packet from a web server VM headed toward a database VM gets checked right where it originates, not at a central appliance somewhere else on the network. No round trip to a dedicated device. Enforcement scales automatically because it lives on every host alongside the workloads it protects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.tigera.io/calico-cloud/network-policy/policy-tiers/tiered-policy" rel="noopener noreferrer"&gt;Calico’s enforcement model&lt;/a&gt; is built on the same principle. On each Kubernetes node, Calico enforces policy at every workload’s network interface using eBPF or one of the Linux netfilter-based dataplanes (iptables or nftables). The mechanism differs from VMware’s kernel module, but the position in the stack is the same: enforcement happens before traffic enters or leaves the workload, not at a central inspection point somewhere else in the cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.tigera.io/calico/latest/about/kubernetes-training/about-ebpf" rel="noopener noreferrer"&gt;eBPF&lt;/a&gt; is worth a brief note here. It’s a way of running purpose-built programs inside the Linux kernel without modifying the kernel itself, which means packet inspection and policy decisions happen at a very low level with very little overhead. It’s the same instinct that makes NSX DFW fast: get enforcement as close to the wire as possible.&lt;/p&gt;

&lt;p&gt;The solution scales in the same way as well. Adding a node to the cluster adds enforcement capacity automatically because the enforcer runs on the node rather than as a separate service that all traffic routes through. Enforcement is distributed across every node, so there’s no central chokepoint that the whole cluster’s traffic has to pass through.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workload-attached Policy
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsbpapax3eaeq12hwbe09.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsbpapax3eaeq12hwbe09.png" alt="Policies follow the workloads they secure" width="800" height="668"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Policies follow the workloads they secure&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When a VM migrates between hosts via &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vsphere/vsphere/8-0/vcenter-and-host-management/migrating-virtual-machines-host-management/migration-with-vmotion-host-management.html" rel="noopener noreferrer"&gt;vMotion&lt;/a&gt;, its firewall rules move with it. The policy is attached to the workload, not the host. This is a consequential architectural choice: you can’t accidentally leave a workload unprotected by moving it to a new host with different configurations in place.&lt;/p&gt;

&lt;p&gt;It also exposes a problem with IP-based firewall rules that becomes obvious the moment workloads start moving. Rules written against specific addresses are tied to a network location, not the workload itself. If a VM changes address after migration, those rules no longer cover it. NSX DFW avoids this by anchoring policy to security group membership instead, and security groups can be built around tags rather than addresses. A VM tagged “web-tier” carries that group membership with it regardless of which host it lands on or what IP it holds. The rule that applies to web-tier VMs follows automatically, with nothing to update.&lt;/p&gt;

&lt;p&gt;This is exactly how Kubernetes and &lt;a href="https://docs.tigera.io/calico-enterprise/latest/network-policy/" rel="noopener noreferrer"&gt;Calico network policies&lt;/a&gt; also work. Rules are declared against label selectors. When a workload moves to a different node, its labels move with it and the policy selector picks it up automatically. Nothing IP-specific lives in the policy, so there’s nothing to update when an address changes. A VM labeled &lt;code&gt;env: production&lt;/code&gt; and &lt;code&gt;tier: database&lt;/code&gt; picks up the right policies wherever it lands. No external mechanism needs to track it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tiered Precedence
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnvnmwcy7hn6e4cocgnzm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnvnmwcy7hn6e4cocgnzm.png" alt="Microsegmentation allows for granular control and a separation of concerns" width="800" height="463"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Microsegmentation allows for granular control and a separation of concerns&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;NSX also has a tiered policy model. Rules are organized into named categories with a fixed evaluation order. Emergency and quarantine rules sit at the top, infrastructure and shared service rules in the middle, and application-level rules at the bottom. Traffic is evaluated against each category in sequence, top down. A deny rule in the highest category stops evaluation immediately. Nothing below it applies.&lt;/p&gt;

&lt;p&gt;The category structure is also where access control is enforced. NSX’s role-based access control can be scoped to specific policy categories. The evaluation order and the permission model are designed to align. Teams with the narrowest access work in the categories that run last, and no rule they write can override a higher category.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.tigera.io/calico-enterprise/latest/network-policy/policy-tiers/" rel="noopener noreferrer"&gt;Calico tiers&lt;/a&gt; are designed according to the same principles. Each tier is a Kubernetes resource with an order field, a number that determines where it sits in the evaluation sequence. Lower numbers run first. A tier with order 100 is evaluated before a tier with order 200, and a deny rule in the first tier cannot be overridden by anything in the second.&lt;/p&gt;

&lt;p&gt;Unlike NSX’s fixed category names, Calico tiers are user-defined. You name them to match your own operational model, such as “security,” “platform,” “application”, and you set the ordering. You can define as many tiers as your use cases require. Most teams structure their tiers so the security team’s policies run first, platform or infrastructure policies run second, and application or namespace-level policies run last.&lt;/p&gt;

&lt;p&gt;Within each tier, individual policies also carry an order field, and within each policy rules are evaluated top to bottom with the first match applying. One useful behaviour worth knowing about is the pass action: a rule can explicitly pass traffic to the next tier rather than allowing or denying it. This lets each tier focus on what it’s responsible for and hand off everything else cleanly, rather than requiring every tier to have an opinion on every packet. The security tier, for example, should not have to worry about whether a specific frontend should be able to talk to a certain backend service.&lt;/p&gt;

&lt;p&gt;For access control, Calico uses Kubernetes-native RBAC. ClusterRoles and ClusterRoleBindings govern access to cluster-scoped resources like tiers and GlobalNetworkPolicy, while Roles and RoleBindings can be used for namespace-scoped NetworkPolicy. Either way, teams can be restricted to working within their own scope with no ability to reach into tiers or namespaces they don’t own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attribute-based Identity
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgwzitmyqdyxnrmp50d94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgwzitmyqdyxnrmp50d94.png" alt="Policies are not IP dependant" width="800" height="441"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Policies are not IP dependant&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;NSX uses tags for dynamic workload grouping. Tags are key-value pairs applied to VMs. You can set them manually through NSX Manager or via API, which means they can be applied at deployment time rather than added by hand after the fact. Security groups define their membership through tag-matching criteria, and that membership is live: any VM carrying the right tag combination joins the group immediately.&lt;/p&gt;

&lt;p&gt;Tagging a VM as “PCI-in-scope” isn’t just a label. It’s a trigger. The moment that tag is applied, the VM joins every security group whose membership rule matches it, and every DFW policy targeting those groups immediately covers the new member. No change request, no waiting. The policy was already written; the workload just identified itself as falling within its scope.&lt;/p&gt;

&lt;p&gt;The same logic works on decommission. When a tag is removed, the VM leaves the group and the rules that covered it stop applying. Orphaned firewall rules accumulating over years of manual changes are a genuine operational hazard in IP-based environments — tags sidestep the problem entirely because security group membership reflects the actual state of the environment, not a record of past changes nobody got around to cleaning up.&lt;/p&gt;

&lt;p&gt;No manual list maintenance. No IP tracking. The tag is the identity.&lt;/p&gt;

&lt;p&gt;NSX tags map to Kubernetes labels, and the mechanism works the same way. Labels are key-value pairs defined in a workload’s manifest and applied at deployment. A &lt;a href="https://docs.tigera.io/calico-enterprise/latest/network-policy/policy-tiers/policy-tutorial-ui" rel="noopener noreferrer"&gt;Calico network policy&lt;/a&gt; references them through a selector: &lt;code&gt;compliance: pci&lt;/code&gt; in a label is picked up by any policy whose selector matches that expression. The moment a workload with that label comes online, it’s covered. No separate step, no group membership to update manually.&lt;/p&gt;

&lt;p&gt;Labels can sit on pods, on namespaces, or on both, which gives you different levels of granularity. A policy that selects on a namespace label applies to everything running in that namespace. A policy that selects on a pod label is more specific. Combining the two lets you write rules like “allow traffic from any workload in the payments namespace that also carries the label &lt;code&gt;tier: web&lt;/code&gt;” — the equivalent of multi-tag security group membership in NSX, expressed as a single selector.&lt;/p&gt;

&lt;p&gt;The decommission property carries over too. When a workload is deleted, its labels go with it. Policies that targeted it stop applying automatically, with no cleanup step and no stale rules accumulating over time.&lt;/p&gt;

&lt;p&gt;For endpoints that can’t carry labels such as external services, legacy VMs not yet migrated, third-party IP ranges, Calico’s NetworkSets handle the case directly. A NetworkSet is a named collection of IP addresses or CIDRs that can be referenced in a policy selector the same way a label can. Rather than repeating IP ranges across every policy that needs them, you reference the NetworkSet by name. When those ranges change, you update the NetworkSet and every policy that references it reflects the change automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  A demo that went sideways in the best way
&lt;/h2&gt;

&lt;p&gt;A solutions engineer was running a demo for a team evaluating a VMware replacement. They were walking through how Calico enforces network policy when the customer’s senior network consultant stopped them.&lt;/p&gt;

&lt;p&gt;This team had been running NSX-T for years. They had 19 T1 edges and 8 segments. During the walkthrough, the consultant started recognizing what he was looking at. Then he pointed out something that reframed the whole conversation. The T1 edges weren’t actually providing any isolation. The distributed firewall policy was doing all the security work.&lt;/p&gt;

&lt;p&gt;What the consultant recognized was that his team had been operating a flat network with policy enforcement. They’d been using NSX tags to classify workloads and superset policies to establish rule precedence.&lt;/p&gt;

&lt;p&gt;Both of those map directly to Calico constructs which surprised them. The assumption had been that moving from NSX to a Kubernetes-native stack meant leaving the policy model behind. It turned out the policy model was already there. They’d been building towards it without realizing it.&lt;/p&gt;

&lt;p&gt;This is a common pattern. In NSX deployments where the DFW has been built out carefully, the network topology handles connectivity and the policy handles security. The T1 edges are default gateways. The actual security boundary is the DFW rule, not the segment boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  What works better on the other side
&lt;/h2&gt;

&lt;p&gt;Once policy is running in Calico, a few things improve over the NSX baseline. Compliance monitoring runs continuously and generates audit-ready reports against frameworks like PCI DSS, HIPAA, SOC 2, and NIST. This replaces manual evidence assembly before each audit cycle. Policy changes tracked in version control produce evidence of reviewed, timestamped, and tamper-evident changes. Tiered RBAC means the security team’s rules are structurally unreachable by teams below, with compliance report access scoped so each team sees only what it owns.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this all means in practice
&lt;/h2&gt;

&lt;p&gt;The NSX distributed firewall may feel like one of the hardest components to replicate but it turns out to be the part that transfers most directly, because the architectural decisions behind it, distributed enforcement, workload identity, tiered governance, attribute-based grouping, are the same decisions Calico was built on.&lt;/p&gt;

&lt;p&gt;The team in that demo was genuinely surprised when they recognized their own security model in the Calico policy framework. They’d spent years building a policy structure in NSX that was a close approximation of what Kubernetes-native policy looks like. They’d been moving toward this model without knowing they were doing it.&lt;/p&gt;

&lt;p&gt;If your NSX security posture is built on tags and tiered rules, you’ve been thinking this way for a while. The migration is a translation project. The model is already there.&lt;/p&gt;

&lt;p&gt;Everything covered here has a corresponding configuration reference in the &lt;a href="https://docs.tigera.io/use-cases/microsegmentation" rel="noopener noreferrer"&gt;Calico microsegmentation documentation&lt;/a&gt;. It walks through tier setup, label-based policy, RBAC, and compliance reporting in detail.&lt;/p&gt;

&lt;p&gt;Read our &lt;a href="https://www.tigera.io/lp/ebook-the-complete-guide-to-vm-networking-for-kubernetes/" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt; for a comprehensive look at VM migration.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.tigera.io/blog/youve-been-running-a-kubernetes-security-model-in-nsx-and-didnt-even-know-it/" rel="noopener noreferrer"&gt;You’ve Been Running a Kubernetes Security Model in NSX and Didn’t Even Know It&lt;/a&gt; appeared first on &lt;a href="https://www.tigera.io" rel="noopener noreferrer"&gt;Tigera – Creator of Calico&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>technicalblog</category>
      <category>vmmigration</category>
      <category>products</category>
    </item>
    <item>
      <title>The New MCP Headers Are a Gift to Gateways</title>
      <dc:creator>Alister Baroi</dc:creator>
      <pubDate>Thu, 06 Aug 2026 20:52:04 +0000</pubDate>
      <link>https://dev.to/tigeraio/the-new-mcp-headers-are-a-gift-to-gateways-5fn0</link>
      <guid>https://dev.to/tigeraio/the-new-mcp-headers-are-a-gift-to-gateways-5fn0</guid>
      <description>&lt;p&gt;In short, buried in the transport section of the &lt;a href="https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/" rel="noopener noreferrer"&gt;MCP 2026-07-28 release candidate&lt;/a&gt; are three changes that matter more to infrastructure teams than to anyone else: mandatory &lt;code&gt;Mcp-Method&lt;/code&gt; and &lt;code&gt;Mcp-Name&lt;/code&gt; headers, cache-control-style &lt;code&gt;ttlMs&lt;/code&gt; and &lt;code&gt;cacheScope&lt;/code&gt; fields, and standardized W3C Trace Context propagation. Together with the stateless core, they turn MCP from a protocol that gateways had to fight into one that meets them halfway. What the headers still don’t carry: who the caller is, whether the call should be allowed, and any record that it happened.&lt;/p&gt;

&lt;p&gt;Everyone is writing about MCP going stateless, and the coverage is deserved. No handshake, no session ID, any request can hit any server replica, round-robin load balancing just works. If you want the deep dive on what that does to protocol state, my colleague Peter is writing one.&lt;/p&gt;

&lt;p&gt;I want to talk about the part of the release candidate that made me sit up, because I spend my days around a gateway that authorizes agent traffic. It’s three transport changes, a few paragraphs in the announcement, and it fixes a problem every MCP-aware proxy has been engineering around since &lt;a href="https://modelcontextprotocol.io/specification/2025-03-26/changelog" rel="noopener noreferrer"&gt;Streamable HTTP shipped in the 2025-03-26 revision&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: MCP was opaque at the network layer
&lt;/h2&gt;

&lt;p&gt;To an HTTP intermediary, MCP traffic used to look like nothing. Every operation, whether it listed tools, read a resource, or transferred money, arrived as a POST to the same endpoint. The thing that actually mattered, the JSON-RPC method and the tool being called, sat inside the request body.&lt;/p&gt;

&lt;p&gt;So any middlebox that wanted to treat a &lt;code&gt;tools/list&lt;/code&gt; differently from a &lt;code&gt;tools/call&lt;/code&gt; had one option: buffer the request, parse the JSON-RPC envelope, and make its decision from the body. That works, and it’s what serious MCP gateways do today. But it puts body parsing on the hot path for every request, including the vast majority where a cheaper answer would have sufficed. Rate-limiters, load balancers, and WAFs that can’t parse JSON-RPC at all were simply blind. Layer 7 infrastructure spent thirty years learning to route on methods and paths, and MCP hid both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mcp-Method and Mcp-Name: Routing without body inspection
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243" rel="noopener noreferrer"&gt;SEP-2243&lt;/a&gt; fixes this at the obvious place. The Streamable HTTP transport now requires &lt;code&gt;Mcp-Method&lt;/code&gt; and &lt;code&gt;Mcp-Name&lt;/code&gt; headers: &lt;code&gt;Mcp-Method&lt;/code&gt; carries the JSON-RPC method (&lt;code&gt;tools/call&lt;/code&gt;, &lt;code&gt;resources/read&lt;/code&gt;, and so on), and &lt;code&gt;Mcp-Name&lt;/code&gt; carries the operation target, such as the tool name. The announcement states the goal plainly: load balancers, gateways, and rate-limiters can route on the operation without inspecting the body.&lt;/p&gt;

&lt;p&gt;What this unlocks, roughly in order of how quickly teams will use it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Per-tool rate limiting.&lt;/strong&gt; Throttle &lt;code&gt;tools/call&lt;/code&gt; for an expensive tool without touching the cheap ones, in a stock rate-limiter that only reads headers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operation-aware routing.&lt;/strong&gt; Send &lt;code&gt;resources/read&lt;/code&gt; to read-optimized replicas, or split list-type traffic from call-type traffic entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cheap early denies.&lt;/strong&gt; A policy gateway can reject a request for a tool that no policy could ever allow before parsing a byte of body.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visibility in existing tooling.&lt;/strong&gt; Access logs, metrics dashboards, and anomaly detection keyed on headers now see MCP operations instead of an undifferentiated stream of POSTs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One design detail deserves attention: servers must reject requests where the headers and the body disagree. That’s what makes the headers usable for real decisions rather than hints. A client can’t advertise &lt;code&gt;tools/list&lt;/code&gt; in the header and smuggle a &lt;code&gt;tools/call&lt;/code&gt; in the body, because the terminating server will refuse it.&lt;/p&gt;

&lt;p&gt;Still, if your gateway makes security decisions, the right way to hold this is defense in depth. The header is a claim the server will eventually verify; the body remains the truth. Fast-deny on headers is always safe, since a mismatch would have been rejected anyway. For the allow path on sensitive operations, an enforcement point should keep parsing the body, both because arguments matter (more on that below) and because “the server behind me validates the invariant my security decision depends on” is a sentence that should make any security engineer reach for their own validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  ttlMs and cacheScope: Caching with permission
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2549" rel="noopener noreferrer"&gt;SEP-2549&lt;/a&gt; gives list results and resource reads two new fields modeled on HTTP &lt;code&gt;Cache-Control: ttlMs&lt;/code&gt; says how long the response stays fresh, and &lt;code&gt;cacheScope&lt;/code&gt; says whether it may be shared across users.&lt;/p&gt;

&lt;p&gt;Gateways have wanted to cache &lt;code&gt;tools/list&lt;/code&gt; forever. Tool catalogs change rarely, agents ask for them constantly, and every wasted round trip adds latency to an agent loop that’s already slow. But caching without server guidance meant guessing, and guessing wrong across users is how one tenant sees another tenant’s tool catalog. &lt;code&gt;cacheScope&lt;/code&gt; makes the safety question explicit, and puts the answer where it belongs: with the server that knows whether the response was personalized.&lt;/p&gt;

&lt;p&gt;If you run a shared MCP gateway in front of internal servers, this is the difference between “we cache nothing because we can’t prove it’s safe” and an actual caching policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trace context: The audit trail gets standard rails
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/modelcontextprotocol/modelcontextprotocol/pull/414" rel="noopener noreferrer"&gt;SEP-414&lt;/a&gt; documents &lt;a href="https://www.w3.org/TR/trace-context/" rel="noopener noreferrer"&gt;W3C Trace Context&lt;/a&gt; propagation in &lt;code&gt;_meta&lt;/code&gt;, fixing the key names for &lt;code&gt;traceparent&lt;/code&gt;, &lt;code&gt;tracestate&lt;/code&gt;, and &lt;code&gt;baggage&lt;/code&gt;. Every SDK and every gateway now agrees on where trace identity lives in an MCP request.&lt;/p&gt;

&lt;p&gt;This one reads like an observability footnote and is quietly the most important of the three for anyone who cares about accountability. Distributed tracing across agent systems mostly worked if you controlled every hop and configured every SDK the same way. The moment traffic crossed a boundary, a different SDK, a vendor’s server, someone else’s gateway, correlation broke, and your trace of “which agent triggered this tool call” ended mid-sentence.&lt;/p&gt;

&lt;p&gt;With the key names fixed in the spec, a trace can survive the full path: agent to gateway to MCP server and back, across implementations, into any OpenTelemetry-compatible backend. For incident response, that’s the difference between “a tool call failed somewhere” and a single trace showing the agent, the gateway’s authorization decision, and the server’s execution as one causal chain. We’ve written before about why &lt;a href="https://www.tigera.io/blog/the-ai-agent-accountability-gap-why-network-policies-api-gateways-and-rbac-are-not-enough/" rel="noopener noreferrer"&gt;multi-hop correlation is where existing tools fall down&lt;/a&gt;; the spec just removed the excuse at the protocol layer.&lt;/p&gt;

&lt;p&gt;And because the protocol is now stateless, all of this composes. A gateway no longer needs session affinity to keep a conversation coherent, so any proxy replica can handle any request, with the operation in the headers and the trace context in &lt;code&gt;_meta&lt;/code&gt;. The 2026-07-28 revision is the first one that reads like its authors had a load balancer diagram on the wall.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the headers still don’t carry
&lt;/h2&gt;

&lt;p&gt;Here’s the boundary, and it’s the same boundary the &lt;a href="https://www.tigera.io/blog/mcps-auth-hardening-what-the-six-new-oauth-seps-fix-and-what-they-still-dont/" rel="noopener noreferrer"&gt;auth-hardening post&lt;/a&gt; ended on. The new transport tells you what is being asked. It does not tell you three other things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who is asking.&lt;/strong&gt; &lt;code&gt;Mcp-Method: tools/call&lt;/code&gt; plus &lt;code&gt;Mcp-Name: transfer_funds&lt;/code&gt; identifies the operation, not the caller. Agent identity has to come from somewhere outside the protocol: mTLS workload identity, a verified token, something the platform issued rather than the process claimed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Whether it should be allowed.&lt;/strong&gt; Headers make policy enforcement cheaper; they don’t supply the policy. “May the finance team’s reporting agent call &lt;code&gt;transfer_funds&lt;/code&gt; with this amount” needs an engine evaluating rules against agent attributes and, for the cases that matter, the arguments in the body. The headers get you to the decision point faster. The decision is still yours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happened.&lt;/strong&gt; A cacheable, traceable, header-routed request that no one records is still unaccountable. Trace context gives audit trails standard rails to run on, but something has to actually write the trail, and it can’t be the agent.&lt;/p&gt;

&lt;p&gt;Read those three again. They are the accountability questions, and the transport section answers none of them, correctly, because a transport shouldn’t. But it is exactly the seam where an agent gateway earns its place. Lynx’s gateway sits on this seam today: every request authorized individually against Cedar policy under a SPIFFE workload identity, with the decision recorded in Agent Trail. The new headers make that architecture cheaper to run and easier to integrate, because the proxy can classify traffic before it parses it, and the trace that leaves the gateway now correlates with everything upstream and downstream by default. Protocol changes rarely hand an enforcement plane this much for free.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you run infrastructure in front of MCP, do this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Update any body-parsing routing or rate-limiting logic to read &lt;code&gt;Mcp-Method&lt;/code&gt; and &lt;code&gt;Mcp-Name&lt;/code&gt; first, and keep body validation on the security-sensitive allow path.&lt;/li&gt;
&lt;li&gt;Audit your caching layer against &lt;code&gt;cacheScope&lt;/code&gt; semantics before enabling cross-user caching of list results. The field tells you when sharing is safe; honor it.&lt;/li&gt;
&lt;li&gt;Wire &lt;code&gt;traceparent&lt;/code&gt; propagation through your proxy now, while the SDKs are shipping support inside the ten-week validation window. Correlation only works if every hop plays.&lt;/li&gt;
&lt;li&gt;Test against the release candidate before July 28. Servers rejecting header/body mismatches is a new failure mode your clients need to handle.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SEP-2243 puts the MCP operation in HTTP headers, so gateways, load balancers, and rate-limiters can act on MCP traffic without parsing JSON-RPC bodies. Servers enforce header/body agreement.&lt;/li&gt;
&lt;li&gt;SEP-2549 adds &lt;code&gt;ttlMs&lt;/code&gt; and &lt;code&gt;cacheScope&lt;/code&gt;, making response caching a documented contract instead of a guess, including whether cross-user sharing is safe.&lt;/li&gt;
&lt;li&gt;SEP-414 standardizes W3C Trace Context in &lt;code&gt;_meta&lt;/code&gt;, so traces survive multi-hop, multi-implementation agent systems end to end.&lt;/li&gt;
&lt;li&gt;The transport now tells intermediaries what is being asked. Identity, authorization, and audit remain the gateway’s job, and the protocol just made that job considerably cheaper to do well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The headers tell you what is being asked. Your gateway decides whether it’s allowed. Your audit trail proves what happened. The spec just handed you the first one for free; the other two are still yours to build, and they were always the hard part.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lynx is Tigera’s security and governance platform for AI agents on Kubernetes: identity, policy, detection, and audit for every agent in your cluster. Read &lt;a href="https://www.tigera.io/blog/how-lynx-works-a-technical-walkthrough/" rel="noopener noreferrer"&gt;How Lynx Works&lt;/a&gt; or request early access at &lt;a href="https://www.tigera.io/demo/?product=lynx" rel="noopener noreferrer"&gt;tigera.io/demo/&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Identity, authorization, and audit are the part the transport leaves to you — and the part that’s hardest to get right. Our whitepaper, &lt;em&gt;Securing AI Agents on Kubernetes using Tigera Lynx&lt;/em&gt;, walks through how to build all three. &lt;a href="https://www.tigera.io/lp/whitepaper-securing-ai-agents-on-kubernetes-using-tigera-lynx/" rel="noopener noreferrer"&gt;Read the whitepaper →&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.tigera.io/blog/the-new-mcp-headers-are-a-gift-to-gateways/" rel="noopener noreferrer"&gt;The New MCP Headers Are a Gift to Gateways&lt;/a&gt; appeared first on &lt;a href="https://www.tigera.io" rel="noopener noreferrer"&gt;Tigera – Creator of Calico&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>technicalblog</category>
      <category>aiagentsecurity</category>
      <category>products</category>
    </item>
    <item>
      <title>VM Migration – What Happens to Your NSX Segments in Kubernetes?</title>
      <dc:creator>Alister Baroi</dc:creator>
      <pubDate>Wed, 05 Aug 2026 19:39:40 +0000</pubDate>
      <link>https://dev.to/tigeraio/vm-migration-what-happens-to-your-nsx-segments-in-kubernetes-30a2</link>
      <guid>https://dev.to/tigeraio/vm-migration-what-happens-to-your-nsx-segments-in-kubernetes-30a2</guid>
      <description>&lt;p&gt;Planning a migration off NSX usually starts with a networking conversation. Segments, VLANs, routing topology and BGP peering are not things that map cleanly to Kubernetes-native constructs the way the NSX distributed firewall maps to Calico’s tiered microsegmentation. NSX virtualizes the network layer in ways that Kubernetes doesn’t replicate by default. There is no native concept of a Layer 2 segment or VLAN, for instance. Pods simply receive IP addresses on a flat, routed network, with no built-in way to give a workload L2 adjacency to external devices or attach it to a specific broadcast domain.&lt;/p&gt;

&lt;p&gt;This is usually where teams start to worry. They can see exactly what NSX is doing for them, but they have no obvious Kubernetes equivalent to point at. The natural question becomes how they will run the networking they depend on once their VMs live in a cluster.&lt;/p&gt;

&lt;p&gt;Achieving the same routing, isolation, and connectivity outcomes, however, is well within reach. It just requires a bit of a mental shift.&lt;/p&gt;

&lt;p&gt;The rest of this blog will cover the details of what that mental shift entails.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F190mo9lmc99mu74a0kvc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F190mo9lmc99mu74a0kvc.png" alt="Each NSX construct and the Calico building block that takes over its job." width="800" height="507"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Each NSX construct and the Calico building block that takes over its job.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How NSX handles segments
&lt;/h2&gt;

&lt;p&gt;Before we get into recreating NSX networking outcomes for VMs in Kubernetes, let’s set the stage by looking at what it is we are trying to recreate.&lt;/p&gt;

&lt;p&gt;NSX is a network virtualization platform that creates software-defined networks (switching, routing, firewalling, load balancing) in a layer that runs on top of existing physical networks. It allows you to provision and segment networks programmatically without reconfiguring switches or routers.&lt;/p&gt;

&lt;p&gt;The building block in all of this is the segment, the logical network a workload attaches to. A segment is not one fixed thing. How it behaves depends on how it is designed. It can be backed by an overlay or tied to a physical VLAN. It reaches the rest of the network either by routing through a Tier-1 gateway or by being advertised externally through a Tier-0 gateway. The backing type is what matters most when you move workloads to Kubernetes, so the two are worth looking at on their own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VLAN-backed segments&lt;/strong&gt; connect to a physical VLAN tag. VMs have L2 adjacency to anything on that VLAN, including physical servers and external devices. The traffic follows the physical VLAN.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overlay segments&lt;/strong&gt; use encapsulation to tunnel L2 frames across a routed underlay, decoupling logical topology from physical VLANs entirely. VMs on the same overlay segment see a flat L2 domain regardless of which hosts they’re on.&lt;/p&gt;

&lt;p&gt;Both types give VMs an IP address, a default gateway, a broadcast domain, and a network identity the rest of the infrastructure can interpret.&lt;/p&gt;

&lt;h2&gt;
  
  
  How each segment type maps to Calico
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Overlay segments&lt;/strong&gt; are the straightforward case. The NSX overlay model, encapsulating L2 traffic and carrying it across a routed underlay, is the same architectural pattern &lt;a href="https://docs.tigera.io/calico/latest/networking/configuring/vxlan-ipip" rel="noopener noreferrer"&gt;Calico uses for its default pod network, with VXLAN or IP-in-IP as the encapsulation format&lt;/a&gt;. VMs and pods communicate across nodes as if they shared a flat L2 domain, the physical network carries UDP, and the details are handled below the application layer. For workloads that were on NSX overlay segments and have no hard dependencies on specific VLANs or IP ranges, the migration to Calico pod networking is largely mechanical. There is nothing to configure specifically for the overlay. It is the default.&lt;/p&gt;

&lt;p&gt;There is one real difference behind that smooth mapping, and it is worth understanding before you migrate. An NSX overlay segment gives workloads a true Layer 2 domain. Calico’s pod network looks flat to the application, but traffic between nodes is actually routed at Layer 3. For almost everything, that distinction never surfaces. The exception is any application that leans on Layer 2 broadcast or multicast to discover its peers, such as some older clustering protocols. Those are the ones to identify up front rather than after the move.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VLAN-backed segments&lt;/strong&gt; are where most migration discussions get complicated, and they are often the most common case, because moving off them takes time and effort that teams under pressure rarely have. A VLAN-backed segment is not just a network assignment. It often carries compliance scope, upstream firewall rules, and IP ranges that legacy systems have relied on for years. Telling a security team that the PCI VLAN is going away requires evidence that the workload’s regulatory scope is preserved by some other mechanism. Telling an external system to update its allowlist requires coordination and change windows. These are organizational constraints as much as technical ones.&lt;/p&gt;

&lt;p&gt;Calico’s answer for environments where VMs need to stay on their VLANs is &lt;a href="https://www.tigera.io/blog/lift-and-shift-vms-to-kubernetes-with-calico-l2-bridge-networks/" rel="noopener noreferrer"&gt;L2 Bridge mode&lt;/a&gt;, configured through a Calico Network resource. It stretches your existing VLAN into the Kubernetes cluster the VM now runs in, so that as far as your network is concerned, the VM is still in its old home with all its dependencies intact. Calico creates a bridge on each cluster node and connects it to the VLAN trunk on the node’s NIC, then attaches the VM to that bridge through an additional interface. The VM sees the same VLAN it was on before the migration. It keeps its IP address. It keeps its MAC address. From the perspective of the upstream switch, the firewall, and any external system talking to that VM, nothing moved.&lt;/p&gt;

&lt;p&gt;The implementation is different but the outcome is the same. Your migrated VM runs on the same VLAN it always did, with the added advantage of now treating your infrastructure as code, with all the version control and management consistency that brings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkieb0k1ivi61ea6q9d9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkieb0k1ivi61ea6q9d9p.png" alt="A VLAN-backed segment becomes a Calico L2 Bridge, so the VM keeps its VLAN, IP, and MAC." width="800" height="480"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A VLAN-backed segment becomes a Calico L2 Bridge, so the VM keeps its VLAN, IP, and MAC.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What about my subnets?
&lt;/h2&gt;

&lt;p&gt;NSX has something called a Tier-1 gateway. It is not a subnet, though it is the closest thing to the gateway behind one. It is the routing tier that segments attach to, and it does a few jobs: it provides the default gateway for workloads on those segments, routes between them and upward toward the Tier-0, keeps tenants isolated at the routing layer, and can hand a workload a fixed source IP via SNAT for allowlisting.&lt;/p&gt;

&lt;p&gt;In Calico, none of this is a single appliance. The IP ranges those segments carried map to the &lt;a href="https://docs.tigera.io/calico/latest/reference/resources/ippool" rel="noopener noreferrer"&gt;IPPool&lt;/a&gt;, the custom resource that defines the CIDRs workloads draw from. The default gateway needs no equivalent: every node already routes for the workloads it hosts, so the T1’s routing simply happens on the nodes. Connectivity between workloads and out to the physical network rides Calico’s own routing instead of a gateway in the path.&lt;/p&gt;

&lt;p&gt;Route isolation, where a T1 keeps one tenant’s routes out of another’s, is handled in Calico Enterprise by &lt;a href="https://www.tigera.io/blog/deep-dive/multi-vrf-support-for-egress-gateways-using-calico/" rel="noopener noreferrer"&gt;Multi-VRF&lt;/a&gt;, implemented through a custom resource called &lt;a href="https://docs.tigera.io/calico-enterprise/latest/reference/resources/externalnetwork" rel="noopener noreferrer"&gt;ExternalNetworks&lt;/a&gt;. Each ExternalNetwork is an independent routing table on the node with its own BGP peering, so routes in one domain stay invisible to another. It is the tool for cases a single routing table cannot handle, such as reaching external services with overlapping address ranges.&lt;/p&gt;

&lt;p&gt;So the Tier-1 equivalent is not one thing you deploy. It is a combination of an IPPool, per-node routing, Multi-VRF where isolation is required, and policy. That is a real shift in thinking, and it works in your favor: no appliance to size, patch, or treat as a single point of failure, routing that scales with the cluster, and every piece a declarative, version-controlled Kubernetes resource rather than a separate box to keep in sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting a segment to the outside world
&lt;/h2&gt;

&lt;p&gt;The NSX Tier-0 gateway is the border between the NSX overlay and the physical network. Its main job is &lt;a href="https://docs.tigera.io/calico/latest/networking/configuring/bgp" rel="noopener noreferrer"&gt;BGP peering&lt;/a&gt; with the top-of-rack switches, advertising the overlay subnets into the physical routing fabric so the rest of the data center knows how to reach NSX-hosted workloads. It is also the exit point for traffic leaving the fabric entirely. The T0 usually runs on a dedicated edge node or edge cluster, often as an active and standby pair, which makes it a concentration point for all north-south traffic. NAT and edge firewalling are T0 functions too, and those can be saved for a follow-up post.&lt;/p&gt;

&lt;p&gt;In Calico, that routing role moves directly onto the cluster nodes. Each node runs a BGP daemon (BIRD) and peers with the physical switching infrastructure through the BGPPeer custom resource, which specifies the peer address, the AS number, and, through a node selector, which nodes participate. Calico advertises pod and VM CIDRs into the fabric exactly as the T0 advertised the overlay subnets. The switches learn routes to cluster workloads and forward to them with no extra configuration on the physical side.&lt;/p&gt;

&lt;p&gt;At scale you do not peer every node with every switch. Calico supports BGP route reflectors, where a small set of nodes hold the peerings and the rest peer with them. This keeps the number of sessions manageable as the cluster grows. Either way, the T0 appliance goes away and the function it performed is distributed across the nodes.&lt;/p&gt;

&lt;p&gt;As with the Tier-1, this is a shift from a box to a behavior, and the trade works in your favor. There is no edge appliance pair to size or fail over, north-south routing no longer funnels through a single concentration point, and capacity scales as you add nodes. The peering itself is declarative which again means that the BGPPeer resource lives in version control and is reviewed like any other part of the cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftk7fdkgo98fcjccs9o8x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftk7fdkgo98fcjccs9o8x.png" alt="The Tier-0 BGP and the Tier-1 routing distribute across the cluster nodes." width="800" height="411"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The Tier-0 BGP and the Tier-1 routing distribute across the cluster nodes.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means in practice
&lt;/h2&gt;

&lt;p&gt;The networking layer is where a migration off NSX looks most different. The segment topology, the gateway appliances, and the BGP configuration do not have one-to-one equivalents on the Kubernetes side, and the first look can be unsettling.&lt;/p&gt;

&lt;p&gt;What carries over is the logic underneath. Overlay segments become Calico pod networking with no special configuration. VLAN-backed segments map to L2 Bridge Networks, so a VM keeps its VLAN, its IP, and its MAC through the move. The T0’s BGP peering moves to the cluster nodes through the BGPPeer resource, and the T1’s routing is absorbed into per-node routing with nothing dedicated to replace it. Where separate routing tables are genuinely needed, Calico Enterprise’s Multi-VRF provides that isolation without a gateway appliance.&lt;/p&gt;

&lt;p&gt;The shape changes but the outcomes do not. Once you stop looking for the appliance and start looking at what it was actually doing, most NSX topologies carry fewer hard dependencies than they first appear, and each of those jobs has a Kubernetes-native home that is declarative, version-controlled, and scales with the cluster. The practical next step is to map your own environment the same way. List what each segment and gateway is really providing. Once that list exists, the Calico equivalents are usually easy to identify. That requirements audit is where most migrations should start, and it is a good conversation to have before the first VM moves.&lt;/p&gt;

&lt;p&gt;If you want to go further, check our our ebook: &lt;a href="https://www.tigera.io/lp/ebook-calico-enterprise-for-nsx-administrators/" rel="noopener noreferrer"&gt;Calico Enterprise for NSX Administrators&lt;/a&gt;. And when you are ready to try the mapping yourself, you can request a Calico Enterprise trial license.&lt;/p&gt;

&lt;p&gt;Ready to migrate VM-centric architectures to Kubernetes platforms? Read our ebook, &lt;a href="https://www.tigera.io/lp/ebook-calico-enterprise-for-nsx-administrators/" rel="noopener noreferrer"&gt;Calico Enterprise for NSX Administrators&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.tigera.io/blog/vm-migration-what-happens-to-your-nsx-segments-in-kubernetes/" rel="noopener noreferrer"&gt;VM Migration – What Happens to Your NSX Segments in Kubernetes?&lt;/a&gt; appeared first on &lt;a href="https://www.tigera.io" rel="noopener noreferrer"&gt;Tigera – Creator of Calico&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>technicalblog</category>
      <category>bestpractices</category>
      <category>vmmigration</category>
      <category>products</category>
    </item>
    <item>
      <title>MCP’s Auth Hardening: What the Six New OAuth SEPs Fix, and What They Still Don’t</title>
      <dc:creator>Alister Baroi</dc:creator>
      <pubDate>Tue, 28 Jul 2026 14:20:20 +0000</pubDate>
      <link>https://dev.to/tigeraio/mcps-auth-hardening-what-the-six-new-oauth-seps-fix-and-what-they-still-dont-2g0c</link>
      <guid>https://dev.to/tigeraio/mcps-auth-hardening-what-the-six-new-oauth-seps-fix-and-what-they-still-dont-2g0c</guid>
      <description>&lt;p&gt;In short, the &lt;a href="https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/" rel="noopener noreferrer"&gt;MCP 2026-07-28 release candidate&lt;/a&gt; is getting attention for going stateless. The quieter story is a package of six SEPs that harden the protocol’s OAuth layer: issuer validation, credential binding, client type declaration, and cleanups around refresh tokens, scopes, and discovery. All six are worth shipping, and all six fix real failure modes. But they harden how a client authenticates to a server, and that was never the whole problem. Agent identity, per-request authorization, delegation, and audit still sit outside the spec. Which means they still sit with you.&lt;/p&gt;

&lt;p&gt;The stateless core is soaking up most of the commentary on the new MCP release candidate, and fair enough: deleting the &lt;code&gt;initialize&lt;/code&gt; handshake and the session ID changes how everyone deploys. But scroll past that section of the announcement and you hit six SEPs of authorization hardening that almost nobody is writing about. That’s a mistake. If you operate MCP servers that hold real credentials, this is the part of the spec that decides whether a confused client hands a token to the wrong party.&lt;/p&gt;

&lt;p&gt;The final spec ships July 28, 2026. The release candidate was locked on May 21, and SDK maintainers are in a ten-week validation window now. So this is a good moment to read the auth changes carefully: late enough that they’re stable, early enough to fix your implementation before the ecosystem expects it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why MCP keeps having to fix auth
&lt;/h2&gt;

&lt;p&gt;MCP inverts the deployment shape OAuth grew up with. Classic OAuth has many clients talking to one authorization server: thousands of apps, one identity provider, one token issuer. MCP runs the other way around. One client, the host application, talks to many servers, each potentially fronted by a different authorization server, discovered at runtime, and often registered on the fly through Dynamic Client Registration.&lt;/p&gt;

&lt;p&gt;That inversion is where the bugs live. The spec authors say it directly: the issuer validation SEP targets “a class of mix-up attack that is more prevalent in MCP’s single-client, many-server deployment pattern.” When your client holds registrations with a dozen authorization servers at once, an attacker doesn’t need to break the crypto. They need to get your client to attribute a response to the wrong server.&lt;/p&gt;

&lt;p&gt;This isn’t the first pass at the problem. The &lt;a href="https://modelcontextprotocol.io/specification/2025-06-18/changelog" rel="noopener noreferrer"&gt;2025-06-18 revision&lt;/a&gt; made Resource Indicators (&lt;a href="https://www.rfc-editor.org/rfc/rfc8707.html" rel="noopener noreferrer"&gt;RFC 8707&lt;/a&gt;) mandatory so tokens are minted for one specific server rather than floating around as bearer credentials any resource might accept. The 2026-07-28 package continues that trajectory: less trust by default, more explicit binding.&lt;/p&gt;

&lt;h2&gt;
  
  
  The six SEPs, grouped by what they protect
&lt;/h2&gt;

&lt;p&gt;The release notes list six SEPs. They cluster into three jobs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Knowing which server you’re actually talking to
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/seps/2468-recommend-issuer-claim-for-auth" rel="noopener noreferrer"&gt;SEP-2468&lt;/a&gt; brings &lt;a href="https://www.rfc-editor.org/rfc/rfc9207.html" rel="noopener noreferrer"&gt;RFC 9207&lt;/a&gt; into MCP: authorization servers include an explicit &lt;code&gt;iss&lt;/code&gt; parameter in authorization responses, and clients that see it must validate it. If the issuer in the response doesn’t match the issuer the client thinks it’s mid-flow with, the client rejects the response. That closes the mix-up attack above. Today including the parameter is recommended rather than required, but the spec is explicit that in a future version clients will be expected to reject responses that omit &lt;code&gt;iss&lt;/code&gt;, so treat it as mandatory in anything you build now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2352" rel="noopener noreferrer"&gt;SEP-2352&lt;/a&gt; fixes the other half of the same confusion. Clients must keep separate registration state per authorization server and bind each registered credential to the issuing server’s &lt;code&gt;issuer&lt;/code&gt; value. If a resource migrates from one authorization server to another, the client re-registers rather than replaying credentials issued by the old server against the new one. Before this, a client could quietly present a client ID minted by issuer A to issuer B, and depending on the server’s tolerance, sometimes it worked. “Sometimes it worked” is not a property you want in an auth system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Telling the authorization server what kind of client you are
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/modelcontextprotocol/modelcontextprotocol/pull/837" rel="noopener noreferrer"&gt;SEP-837&lt;/a&gt; is small and will quietly fix a failure everyone building a local MCP client has hit. Clients now declare their OpenID Connect &lt;code&gt;application_type&lt;/code&gt; during Dynamic Client Registration. Without it, authorization servers commonly default a desktop or CLI client to &lt;code&gt;web&lt;/code&gt; and then reject its &lt;code&gt;localhost&lt;/code&gt; redirect URI, because web clients aren’t supposed to redirect to localhost. The result was a class of registration failures that looked like server bugs but were really a missing field. If you’ve ever watched a CLI tool fail OAuth registration against a strict identity provider for no visible reason, this SEP is for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Housekeeping that removes guesswork
&lt;/h3&gt;

&lt;p&gt;The remaining three SEPs are clarifications, and clarifications in auth specs matter more than they sound. &lt;a href="https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2207" rel="noopener noreferrer"&gt;SEP-2207&lt;/a&gt; documents how to request refresh tokens from OpenID Connect style authorization servers, which implementers previously handled with folklore and copy-paste. &lt;a href="https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2350" rel="noopener noreferrer"&gt;SEP-2350&lt;/a&gt; pins down scope accumulation during step-up authentication, so a client that goes back for more scopes knows what happens to the ones it already has. &lt;a href="https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2351" rel="noopener noreferrer"&gt;SEP-2351&lt;/a&gt; clarifies the &lt;code&gt;.well-known&lt;/code&gt; discovery suffix behavior, which sounds cosmetic until two SDKs disagree about where the metadata document lives and interop breaks.&lt;/p&gt;

&lt;p&gt;Here’s the package in one view:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;SEP&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;What it requires&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Failure it prevents&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2468&lt;/td&gt;
&lt;td&gt;Validate &lt;code&gt;iss&lt;/code&gt; on authorization responses (RFC 9207)&lt;/td&gt;
&lt;td&gt;Mix-up attacks across multiple authorization servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2352&lt;/td&gt;
&lt;td&gt;Bind registered credentials to their issuer; re-register on migration&lt;/td&gt;
&lt;td&gt;Credential replay against the wrong authorization server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;837&lt;/td&gt;
&lt;td&gt;Declare &lt;code&gt;application_type&lt;/code&gt; during Dynamic Client Registration&lt;/td&gt;
&lt;td&gt;Desktop/CLI clients rejected over localhost redirect URIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2207&lt;/td&gt;
&lt;td&gt;Documented refresh token flow for OIDC-style servers&lt;/td&gt;
&lt;td&gt;Divergent, improvised token renewal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2350&lt;/td&gt;
&lt;td&gt;Defined scope accumulation in step-up flows&lt;/td&gt;
&lt;td&gt;Ambiguity about previously granted scopes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2351&lt;/td&gt;
&lt;td&gt;Clarified &lt;code&gt;.well-known&lt;/code&gt; discovery suffix&lt;/td&gt;
&lt;td&gt;Metadata discovery interop failures&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you maintain an MCP client or server, the work here is real but bounded, and the Tier 1 SDKs are expected to ship support within the validation window. Adopt it all. None of it is controversial.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the six SEPs still don’t touch
&lt;/h2&gt;

&lt;p&gt;Now the part I actually want to talk about. Read the package again and notice what every one of these SEPs has in common: they harden the exchange between one OAuth client and one authorization server. That exchange needed hardening. But if you’re responsible for a production deployment of agents, it’s worth being precise about the questions this spec revision does not answer, because the gap between “OAuth done right” and “agents governed” is where incidents happen. We mapped that gap in the &lt;a href="https://www.tigera.io/blog/the-ai-agent-accountability-gap-why-network-policies-api-gateways-and-rbac-are-not-enough/" rel="noopener noreferrer"&gt;accountability gap post&lt;/a&gt;; the new spec moves none of these boundaries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The token authenticates the client, not the agent&lt;/strong&gt;. In MCP’s OAuth model, the thing holding the credential is the host application. The agent, the actual decision-making process that chose to call the tool, has no identity of its own anywhere in the flow. Two hundred agents behind one host share one client identity. When a token is misused, “which client” has an answer and “which agent, on whose behalf, deciding on the basis of what” does not. Issuer binding makes the client-to-server link trustworthy; it says nothing about what’s behind the client.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scopes are not policy&lt;/strong&gt;. OAuth answers admission: does this client get a token for this server. It does not answer the per-request question: should this agent be allowed to call this tool, with these arguments, right now. A scope like &lt;code&gt;mcp:tools&lt;/code&gt; is a key to the building, not a rule about what you may do inside. Real deployments need decisions at the level of “the finance team’s reporting agent may call &lt;code&gt;read_ledger&lt;/code&gt; but never &lt;code&gt;transfer_funds&lt;/code&gt;, and never with an amount above X.” Nothing in this spec revision, or any planned one, evaluates that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delegation chains vanish&lt;/strong&gt;. Agents call agents, which call MCP servers. Each hop can be individually OAuth-clean while the chain as a whole is unaccountable: the server at the end sees a valid token from its immediate caller and nothing else. Who initiated the chain, under whose authority, through which intermediaries? The protocol doesn’t carry that provenance, and hardened issuer validation doesn’t create it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nobody is required to write anything down&lt;/strong&gt;. All six SEPs make the authorization decision more trustworthy. None of them require a record of the decision. When your compliance team asks “was this tool call authorized by policy, and which policy,” a perfectly spec-compliant deployment gives you nothing. The spec is silent on audit, deliberately: it’s a protocol, not a governance framework. That’s the correct scoping decision for a protocol. It just means the governance framework has to come from somewhere else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fleet scale multiplies all of it&lt;/strong&gt;. One client and many servers is the spec’s model. An enterprise runs many clients and many servers: N agents times M servers, each pair needing registration, each registration now correctly issuer-bound. With ten agents you can manage that by hand. With a hundred you have a spreadsheet nobody trusts. With a thousand, you need a registry, and the spec has no opinion about registries, or about noticing the agent that never registered at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardened plumbing still needs governance
&lt;/h2&gt;

&lt;p&gt;None of this is a criticism of the SEPs. Protocol specs should be narrow, and this package does exactly what a protocol revision should: it makes the mechanics harder to get wrong. MCP without issuer validation was HTTP without certificate checking; now that hole is closing.&lt;/p&gt;

&lt;p&gt;But the pattern to notice is that every question the spec leaves open is a question the agent could answer dishonestly if you ask the agent. Which agent are you? What are you allowed to do? Who told you to do this? The only place those answers stay trustworthy is in the environment around the agent: workload identity issued by the platform rather than claimed by the process, policy evaluated per request at a gateway the traffic can’t bypass, and an audit trail written by the infrastructure rather than the workload. That’s the argument we’ve been making since &lt;a href="https://www.tigera.io/blog/why-we-built-lynx-bringing-control-to-the-age-of-ai-agents/" rel="noopener noreferrer"&gt;Why We Built Lynx&lt;/a&gt;, and it’s how Lynx is built: SPIFFE-based identity per agent, Cedar policy evaluated on every request, and a decision trail that exists whether or not the agent cooperates. The new spec makes the OAuth layer under all of that meaningfully stronger, and changes the division of labor not at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The 2026-07-28 auth package is six SEPs: issuer validation (2468), issuer binding for credentials (2352), &lt;code&gt;application_type&lt;/code&gt; in Dynamic Client Registration (837), plus documented refresh tokens (2207), scope accumulation (2350), and &lt;code&gt;.well-known discovery&lt;/code&gt; (2351).&lt;/li&gt;
&lt;li&gt;The common thread is binding: responses bound to issuers, credentials bound to issuers, clients declaring what they are. Mix-up and replay attacks in the one-client-many-servers pattern get materially harder.&lt;/li&gt;
&lt;li&gt;Adopt all of it now. &lt;code&gt;iss&lt;/code&gt; validation is heading toward mandatory, and SDK support lands within the ten-week validation window.&lt;/li&gt;
&lt;li&gt;Be clear-eyed about scope: this hardens client-to-server authentication. Agent identity, per-request authorization, delegation provenance, and audit remain outside the protocol, by design.&lt;/li&gt;
&lt;li&gt;Those four gaps are the governance layer. If your plan for them is “the spec will get there eventually,” it won’t, and it shouldn’t. Protocols carry messages; platforms enforce policy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So update your SDKs, validate &lt;code&gt;iss&lt;/code&gt;, bind your registrations. Then ask the question the spec was right not to answer. When a token you issued gets used for a tool call you’d never have approved, presented by an agent you can’t name, who catches it, and where is the record? (&lt;a href="https://www.tigera.io/blog/five-principles-of-an-accountable-ai-agent-network-how-to-evaluate-any-governance-platform/" rel="noopener noreferrer"&gt;Five Principles of an Accountable AI Agent Network&lt;/a&gt; is the checklist for evaluating whatever answers you get.)&lt;/p&gt;

&lt;p&gt;The spec authenticates the client. Someone still has to govern the agent.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The same release candidate also quietly handed infrastructure teams the best transport upgrade MCP has had. That story deserves its own post, and it gets one soon. In the meantime, the whitepaper &lt;a href="https://www.tigera.io/lp/whitepaper-securing-ai-agents-on-kubernetes-using-tigera-lynx/" rel="noopener noreferrer"&gt;Securing AI Agents on Kubernetes using Tigera Lynx&lt;/a&gt; is the long-form version of the argument this post ends on: what identity, policy, and audit look like when the environment enforces them instead of the agent.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Read our whitepaper, &lt;a href="https://www.tigera.io/lp/whitepaper-securing-ai-agents-on-kubernetes-using-tigera-lynx/" rel="noopener noreferrer"&gt;Securing AI Agents on Kubernetes using Tigera Lynx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.tigera.io/blog/mcps-auth-hardening-what-the-six-new-oauth-seps-fix-and-what-they-still-dont/" rel="noopener noreferrer"&gt;MCP’s Auth Hardening: What the Six New OAuth SEPs Fix, and What They Still Don’t&lt;/a&gt; appeared first on &lt;a href="https://www.tigera.io" rel="noopener noreferrer"&gt;Tigera – Creator of Calico&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>technicalblog</category>
      <category>aiagentsecurity</category>
    </item>
    <item>
      <title>Bring Your Own VLAN: Moving VMs to Kubernetes Without Changing a Single IP</title>
      <dc:creator>Alister Baroi</dc:creator>
      <pubDate>Tue, 28 Jul 2026 13:55:10 +0000</pubDate>
      <link>https://dev.to/tigeraio/bring-your-own-vlan-moving-vms-to-kubernetes-without-changing-a-single-ip-106p</link>
      <guid>https://dev.to/tigeraio/bring-your-own-vlan-moving-vms-to-kubernetes-without-changing-a-single-ip-106p</guid>
      <description>&lt;p&gt;For many organizations, modernizing their VMs before migrating them is not a realistic option, especially when external events trigger the migration. Mapping dependencies and refactoring network configurations before the deadline is impractical, forcing VMs to move as they are.&lt;/p&gt;

&lt;p&gt;The mechanics of moving a VM are largely solved. Tools like Forklift handle what a vSphere admin would recognize as a cold or warm migration: copy the VMDKs off the datastore, convert the guest, and boot it as a &lt;a href="https://www.tigera.io/learn/guides/kubevirt/" rel="noopener noreferrer"&gt;KubeVirt&lt;/a&gt; VM on Kubernetes. The guest comes through with its disks, its OS, its MAC address, and the static IP still written in its network configuration.&lt;/p&gt;

&lt;p&gt;Recreating the NSX segment the vNIC was attached to, the VLAN that defined the VM’s compliance scope, or the firewall rules that reference its address is a different story. The VM arrives in a cluster that knows nothing about any of it. Everything NSX was doing for that VM now has to be rebuilt on the Kubernetes side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.tigera.io/learn/guides/kubernetes-networking/" rel="noopener noreferrer"&gt;Kubernetes networking&lt;/a&gt; cannot solve this on its own, for two reasons. First, pod IPs are assigned dynamically from the cluster’s pod CIDR, and a KubeVirt VM attached to the pod network is treated like any other workload, meaning that it gets a fresh address on arrival and again whenever it reschedules, so the static IP in its configuration file is unusable. Second, the pod network is routed. The model has no concept of a VLAN, a broadcast domain, or a persistent MAC address. A VM whose compliance scope or firewall rules depend on VLAN membership needs L2 semantics that the standard Kubernetes stack was never designed to carry.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdcq8dbvhgmhgj69e8pdd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdcq8dbvhgmhgj69e8pdd.jpg" alt="Migrating a VM with default Kubernetes networking breaks dependencies" width="800" height="354"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Migrating a VM with default Kubernetes networking breaks dependencies&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  L2 vs L3: why the mismatch matters
&lt;/h2&gt;

&lt;p&gt;Traditional VM networking is rooted in L2. A VM on a VLAN-backed segment sits in a broadcast domain: when it needs to reach anything outside its subnet, it broadcasts an ARP request for its default gateway, and the gateway answers with its MAC address because they share that domain. The upstream switch learns the VM’s own MAC the same way, and its static IP belongs to the VLAN’s subnet.&lt;/p&gt;

&lt;p&gt;Two VMs on the same segment talk to each other without a router ever being involved. The pod network offers none of this, not because Kubernetes is deficient but because it was scoped for workloads designed for a routed network from the start. Running an L2-dependent VM in a cluster means extending L2 into the cluster explicitly, and that is where Calico’s L2 Bridge comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why VMs may need to stay on their original VLAN
&lt;/h2&gt;

&lt;p&gt;The short answer is that the VLAN is rarely just a VLAN. Over time it accumulates dependencies that make changing it expensive in ways that have nothing to do with technology.&lt;/p&gt;

&lt;p&gt;The most common is compliance scope. In PCI DSS environments, the cardholder data environment is often defined by VLAN membership. The audit evidence says “these workloads are in scope because they are on VLAN 200.” Moving a workload off that VLAN and redefining scope through a different mechanism, such as labels, policy selectors, and egress controls, is possible, but it requires re-engaging the auditor, updating the scoping documentation, and getting sign-off before the workload moves. That process runs on a different timeline than a migration project.&lt;/p&gt;

&lt;p&gt;Upstream firewall rules are the other major dependency. Perimeter firewalls and network ACLs are typically written against subnets and VLAN ranges, not individual IPs. A rule that permits traffic from 10.0.2.0/24 to the payment processor is a rule about VLAN 200. Changing the workload’s IP or VLAN means finding every rule that references that range, across every firewall that sits between that workload and anything it talks to, and updating them in coordination. Each of those updates requires a scheduled change window and with it the usual requests, approvals, and rollback plans.&lt;/p&gt;

&lt;p&gt;Beyond compliance and firewalls, there are the smaller dependencies that pile up over years of operations. Monitoring systems with hardcoded IPs, application configuration files that reference addresses directly, license servers that are locked to a specific IP, and external SaaS integrations that have the service IP on an allowlist are just a few examples. None of these are hard to fix individually. Together they represent enough coordination overhead that “just keep the IP” is often the right engineering decision, not a workaround.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Calico preserves VLAN-backed segment outcomes
&lt;/h2&gt;

&lt;p&gt;The three categories of dependency from the previous section all trace back to the same requirement: the VM needs to keep its IP address, its VLAN membership, and its MAC address after it moves. If those three things are preserved, nothing upstream needs to change.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.tigera.io/blog/lift-and-shift-vms-to-kubernetes-with-calico-l2-bridge-networks/" rel="noopener noreferrer"&gt;Calico L2 Bridge Networks&lt;/a&gt; preserve all three. Instead of attaching the VM to the routed pod network, a Network resource tells Calico to connect the VM’s vNIC to a Linux bridge on the cluster node. The bridge is easiest to understand as the node’s vSwitch: the physical NIC is the uplink, and a VLAN sub-interface on that NIC plays the role of the tagged port group that selects the VM’s VLAN out of the trunk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkewv8k67qo1cgs6nip70.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkewv8k67qo1cgs6nip70.jpg" alt="The VM connects to its old VLAN with a bridge" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The VM connects to its old VLAN with a bridge&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There is one physical prerequisite. The node’s NIC must be plugged into a switch trunk port that carries the VM’s VLAN, just as an ESXi host’s uplinks must be trunked for a VLAN-backed segment to work. If VLAN 200 has not been extended to the Kubernetes nodes, there is no broadcast domain for the VM to join.&lt;/p&gt;

&lt;p&gt;With that in place, the VM sits in the same broadcast domain it occupied on ESXi. It ARPs for its gateway and the same physical gateway answers. The auditor still sees the workload on VLAN 200, the firewall still sees traffic from the same IP and subnet, and the license server, the monitoring system, and the application’s own configuration all still point at an address that hasn’t changed. The only real difference is that the VM now runs on a Kubernetes node instead of an ESXi host, with its network attachment declared as a Kubernetes resource instead of an NSX segment. The next section covers how the bridge is actually constructed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Calico L2 Bridge works
&lt;/h2&gt;

&lt;p&gt;KubeVirt runs each VM inside a pod. When that pod is created, the cluster assigns it an IP from the pod CIDR like any other pod. The VM then boots inside it, and its virtual NIC is normally wired into the pod’s network namespace, so the VM inherits the pod’s identity: a cluster-assigned address on a routed network where its original IP means nothing. Calico L2 Bridge Networks change that wiring step: instead of connecting the vNIC to the pod network interface, Calico connects it to a Linux bridge on the node.&lt;/p&gt;

&lt;p&gt;When a Network resource defines an L2 bridge network and associates it with a VLAN, Calico sets up a Linux bridge on the cluster node. That bridge has an uplink to the VLAN sub-interface on the node’s physical NIC, which gives it access to the trunk the node is already connected to. The VM’s virtual NIC is attached to that bridge through a tap interface, the Linux equivalent of the vSwitch port a vNIC plugs into on ESXi. The result is a direct L2 path from the VM to the physical switching fabric: VM tap device, through the Linux bridge, through the VLAN sub-interface, out the physical NIC, into the same broadcast domain the VM was on before.&lt;/p&gt;

&lt;p&gt;The upstream switch learns the VM’s MAC address the same way it always has, by watching frames arrive on the VLAN. From the switch’s perspective, a new port came up carrying a MAC it already knew. Nothing in the physical network needs to be reconfigured.&lt;/p&gt;

&lt;p&gt;This is the plumbing a migration plan needs to account for, and in practice it comes down to a short checklist. Before the move, record the VM’s VLAN and IP address. Create a Network resource referencing that VLAN, and point the VM’s KubeVirt definition at it by name. When the VM starts on its new node, Calico builds the bridge, the guest boots with the network configuration it has always had, and traffic flows on the same VLAN as before. As far as the VM knows, it’s still where it always was.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the network, move the machine
&lt;/h2&gt;

&lt;p&gt;None of the dependencies this blog started with have to be rewired when your VM moves to a Kubernetes cluster. The auditor’s scoping document, the perimeter firewall rules, the license server record, the hardcoded addresses in a decade of operational tooling: all of it still holds, because the VM still has the IP, the MAC address, and the VLAN membership they were written against. The migration moves the machine without touching the network identity everything else depends on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzga4b3309w9opignpfsb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzga4b3309w9opignpfsb.jpg" alt="The VM keeps its place on the VLAN and its dependencies" width="800" height="354"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The VM keeps its place on the VLAN and its dependencies&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There is a broader point here that goes beyond any single VM. Because the VLAN extends into the cluster, ESXi and Kubernetes stop being two separate worlds with a hard cutover between them. During a migration they are just two sets of hosts attached to the same networks, which means you can move one VM this week, ten next month, and leave the stubborn ones where they are for now. The workloads that have moved keep talking to the ones that haven’t, and if something goes wrong, rolling back is a matter of booting the VM on the other side again. That changes what an NSX team is actually signing up for. The migration stops being a leap to an unfamiliar platform and becomes a gradual shift of workloads across a network you still recognize and still control, with the Kubernetes learning curve spread over months instead of compressed into a cutover weekend.&lt;/p&gt;

&lt;p&gt;If a phased migration like that is what you are planning, read our ebook: &lt;a href="https://www.tigera.io/lp/ebook-calico-enterprise-for-nsx-administrators/" rel="noopener noreferrer"&gt;Calico Enterprise for NSX Administrators&lt;/a&gt;. When you are ready to try Calico yourself, you can &lt;a href="https://www.calicocloud.io/home" rel="noopener noreferrer"&gt;get started for free&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ready to migrate VM-centric architectures to Kubernetes platforms? Read our ebook, &lt;a href="https://www.tigera.io/lp/ebook-calico-enterprise-for-nsx-administrators/" rel="noopener noreferrer"&gt;Calico Enterprise for NSX Administrators&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.tigera.io/blog/bring-your-own-vlan-moving-vms-to-kubernetes-without-changing-a-single-ip/" rel="noopener noreferrer"&gt;Bring Your Own VLAN: Moving VMs to Kubernetes Without Changing a Single IP&lt;/a&gt; appeared first on &lt;a href="https://www.tigera.io" rel="noopener noreferrer"&gt;Tigera – Creator of Calico&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>technicalblog</category>
      <category>bestpractices</category>
      <category>vmmigration</category>
      <category>products</category>
    </item>
    <item>
      <title>Why eBPF Is Useful for Watching and Sandboxing AI Agents</title>
      <dc:creator>Alister Baroi</dc:creator>
      <pubDate>Mon, 27 Jul 2026 20:04:42 +0000</pubDate>
      <link>https://dev.to/tigeraio/why-ebpf-is-useful-for-watching-and-sandboxing-ai-agents-12pp</link>
      <guid>https://dev.to/tigeraio/why-ebpf-is-useful-for-watching-and-sandboxing-ai-agents-12pp</guid>
      <description>&lt;p&gt;Most of our runtime security habits were built for deterministic workloads. A service does what its code says: review the code, sign the image, and its behavior is bounded. Agents are different. An agent’s behavior emerges from a model reasoning over whatever lands in its context window, and some of that context comes from places we don’t fully control — a retrieved document, a tool’s output, a user’s prompt. Meanwhile the agent usually runs with real privileges: a service account, network reach, mounted secrets, a filesystem. When untrusted input shapes behavior, those privileges get exercised less predictably than we’re used to.&lt;/p&gt;

&lt;p&gt;A lot of good work goes into making agents harder to mislead — prompt hygiene, injection classifiers, guardrail models. It’s worth pairing that with a second question: if an agent does something we didn’t intend, how far can it actually reach? That’s blast radius, and it’s mostly a decision we make at the runtime layer, independent of how the prompt was handled.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://www.tigera.io/learn/guides/ebpf/" rel="noopener noreferrer"&gt;eBPF&lt;/a&gt; is a good fit, for two reasons: it’s an excellent way to &lt;em&gt;see&lt;/em&gt; what an agent is doing, and it can &lt;em&gt;enforce&lt;/em&gt; limits on what the agent can touch — without changing the agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Most of what an agent does is a syscall
&lt;/h2&gt;

&lt;p&gt;The reason the kernel is a natural place to observe and constrain an agent is that nearly everything an agent does — reading a file, running a command, opening a connection — resolves to a syscall. eBPF attaches to the kernel, beneath the workload, so it can watch those syscalls directly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;File activity —&lt;/strong&gt; via LSM hooks (BPF-LSM / KRSI) or kprobes on &lt;code&gt;openat, read, write&lt;/code&gt;. You can see exactly what an agent reads and writes, including attempts to reach paths like &lt;code&gt;/var/run/secrets&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process activity —&lt;/strong&gt; via &lt;code&gt;sched_process_exec&lt;/code&gt; and LSM &lt;code&gt;bprm_*&lt;/code&gt; hooks. You can see every binary an agent tries to execute, including an unexpected shell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network activity —&lt;/strong&gt; via cgroup &lt;code&gt;connect&lt;/code&gt; hooks, tc/XDP, and DNS visibility. You can see where an agent is trying to send traffic, and whether it’s trying to bypass your model gateway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That alone is valuable: a clear, tamper-resistant record of what your agents actually do at runtime, without instrumenting them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnmu8fu8by1e9vle8arbw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnmu8fu8by1e9vle8arbw.png" width="682" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  From observing to enforcing
&lt;/h2&gt;

&lt;p&gt;eBPF can also act inline — overriding a syscall or stopping a process when a policy is violated. That means the same layer supports a natural progression from &lt;strong&gt;observe → alert → enforce&lt;/strong&gt; , and you can move an agent along that path without redeploying it.&lt;/p&gt;

&lt;p&gt;In practice, that gives you sandbox-like controls expressed as simple rules about what a process may touch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow reads under &lt;code&gt;/workspace&lt;/code&gt; but keep writes off for a read-only agent, and keep secrets out of reach entirely.&lt;/li&gt;
&lt;li&gt;Pin an agent to an allow-list of binaries, or block &lt;code&gt;exec&lt;/code&gt; for agents that should never spawn a shell.&lt;/li&gt;
&lt;li&gt;Require egress to flow through your model gateway, and block anything trying to go direct.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two properties make this practical at scale. First, because eBPF sits beneath the workload, it applies to pods that are &lt;strong&gt;already running&lt;/strong&gt; and agents you &lt;strong&gt;haven’t modified&lt;/strong&gt; — no recompile, no sidecar, no new base image. That’s a real advantage when you already have agents in production. Second, it runs in a different trust domain than the workload, so it’s hard for the agent to tamper with, and the overhead is low enough to run fleet-wide.&lt;/p&gt;

&lt;h2&gt;
  
  
  What eBPF is not for
&lt;/h2&gt;

&lt;p&gt;eBPF is well suited to blast-radius questions — &lt;em&gt;what a process may touch&lt;/em&gt; — rather than deep content inspection. It can see that an agent is opening a connection or writing a path and act on that, but it can’t understand the semantics of a prompt or redact fields inside an encrypted request body. That kind of work belongs at a TLS-terminating proxy at the model gateway that can do semantic checks on the agent’s intent and actions. eBPF helps contain what an agent can &lt;em&gt;do&lt;/em&gt;; a proxy governs what it &lt;em&gt;sends&lt;/em&gt;. They’re most useful together, and eBPF is happiest as a uniform floor beneath whatever higher-fidelity, application-aware controls you already run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftu94blza1zwsj77nuch1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftu94blza1zwsj77nuch1.png" width="800" height="959"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Agents behave less predictably than the workloads our tooling was designed for, so it’s worth planning for the moments when one does something unintended. eBPF gives you two things that can help: visibility into what agents are actually doing at the syscall level, and a tamper-resistant way to lock them down, across agents you never had to change or redeploy.&lt;/p&gt;

&lt;p&gt;Want to see what this looks like in practice on Kubernetes? Get the ebook: &lt;a href="https://www.tigera.io/lp/whitepaper-securing-ai-agents-on-kubernetes-using-tigera-lynx/" rel="noopener noreferrer"&gt;Securing AI Agents on Kubernetes using Tigera Lynx&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.tigera.io/blog/why-ebpf-is-useful-for-watching-and-sandboxing-ai-agents/" rel="noopener noreferrer"&gt;Why eBPF Is Useful for Watching and Sandboxing AI Agents&lt;/a&gt; appeared first on &lt;a href="https://www.tigera.io" rel="noopener noreferrer"&gt;Tigera – Creator of Calico&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>technicalblog</category>
      <category>aiagentsecurity</category>
      <category>bestpractices</category>
    </item>
    <item>
      <title>Migrate First, Modernize Later: A Leadership Guide to Converging VMs and Containers to Run AI Workloads</title>
      <dc:creator>Alister Baroi</dc:creator>
      <pubDate>Fri, 24 Jul 2026 21:32:47 +0000</pubDate>
      <link>https://dev.to/tigeraio/migrate-first-modernize-later-a-leadership-guide-to-converging-vms-and-containers-to-run-ai-4l52</link>
      <guid>https://dev.to/tigeraio/migrate-first-modernize-later-a-leadership-guide-to-converging-vms-and-containers-to-run-ai-4l52</guid>
      <description>&lt;h2&gt;
  
  
  The Tipping Point
&lt;/h2&gt;

&lt;p&gt;Every so often the ground under enterprise IT moves. It’s moving now. Across industries, organizations are consolidating fragmented infrastructure onto a single, self-hosted platform capable of running both containers and virtual machines side by side. The motivation is simple: simplify operations, lower cost and reallocate resources &amp;amp; budget to AI initiatives. Kubernetes is emerging as the primary platform for many of these workloads.&lt;/p&gt;

&lt;p&gt;For most IT leaders, the compute and storage portions of a VM migration are manageable. Storage arrays and hypervisor CPU/memory allocation translate fairly directly to Kubernetes equivalents. Networking is where migration plans stall. A VM’s network identity — its IP, its VLAN membership, its firewall rules — is wired into surrounding infrastructure, monitoring, compliance controls, and business processes that nobody wants to touch during a migration window.&lt;/p&gt;

&lt;p&gt;Teams accustomed to NSX for this work find that native Kubernetes networking wasn’t built with VM administrators in mind, and the functionality gap becomes the reason migration projects get bigger or are stalled. If the networking problem is solved — if a VM can move to Kubernetes and keep its IP, its policy, and its security posture intact — then the rest of the platform consolidation stops being an expensive, multi-year architectural bet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Power of Convergence&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
eBPF-powered Calico unified platform’s value has always been convergence and portability — collapsing separate networking domains into a platform and enabling customers to avoid vendor lock-in by platform vendors.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Networking and Security Model for Any Kubernetes Distribution, Any Workload, Anywhere
&lt;/h2&gt;

&lt;p&gt;Calico Enterprise was developed to deliver three objectives for enterprise networking &amp;amp; security in Kubernetes in a single unified platform to enable enterprises to be ready for hosting AI workloads. Now with the launch of Calico for VMs on Kubernetes, Calico delivers a fourth objective: support for VMs &amp;amp; Containers on Kubernetes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;All the components required for k8s networking and network security in a single unified platform –&lt;/strong&gt; Built on the most trusted open-source technologies in Kubernetes — Calico Open Source, Istio, Envoy, and eBPF — the Calico platform gives platform engineering teams a single management plane to enforce, observe, and troubleshoot all workload communication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified platform works across any Kubernetes distribution –&lt;/strong&gt; Calico is Kubernetes agnostic, and supports all major distributions equally: AKS, EKS, GKE, VMware VKS, RedHat OpenShift, Canonical, SUSE, Mirantis and others. That matters strategically for a VM migration decision: it means the move of workloads from a legacy hypervisor to Kubernetes doesn’t lock the organization into a single Kubernetes vendor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified platform works across infrastructure –&lt;/strong&gt; Calico extended one unified model across on-premises, cloud environments and edge, so connectivity, security, and observability behave identically no matter where a cluster runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified platform works across different types of workloads – Containers &amp;amp; VMs –&lt;/strong&gt; Calico enables virtual machines and containers to share one networking fabric, one policy model, and one observability plane. Seamless VM migration while maintaining L2 networking, and a single control plane for ease of management, regardless of the workload, VMs or containers.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Benefits of Adopting a Modern Networking Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Migrate First, Modernize Later
&lt;/h3&gt;

&lt;p&gt;The single most important idea for a migration plan to succeed is separating “get off legacy” from “redesign the network and workloads”. Trying to do both at once is what turns a migration into a multi-year modernization program. Calico’s L2 bridge capability extends existing VLANs into Kubernetes, so a VM can move to a Kubernetes cluster on day one without renumbering, without rewriting firewall rules, and without breaking the hard-coded dependencies — DNS records, monitoring agents, compliance scans — that assume a specific IP or subnet. Once workloads are safely on Kubernetes, the network architecture can evolve on its own timeline — moving from an L2, VLAN-based design to a Kubernetes-native L3 design when the team is ready, not because the migration forced the issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  A complete stack for VM networking on Kubernetes
&lt;/h3&gt;

&lt;p&gt;Every capability and outcome delivered by NSX has a direct Kubernetes-native counterpart in Calico:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connect –&lt;/strong&gt; Calico Networks provide connectivity to VM workloads and to the networks and services around them. L2 Bridge capabilities can extend existing network VLANs (Segments) into Kubernetes for workloads that require Layer 2 or network continuity during and after migration. BGP-based routing, egress gateway, load balancing and ingress gateway functions support delivering applications and services to consumers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure –&lt;/strong&gt; Calico network policy, policy tiers, staged policy and DNS policy provide Kubernetes-native controls for access enforcement and microsegmentation. Policies can be planned, monitored and validated before enforcement, helping teams maintain security posture as workloads move and apply consistent controls across VMs and containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observe –&lt;/strong&gt; Calico Service Graph, flow logs, DNS logs, L7 visibility and packet capture provide context for troubleshooting and security operations. Teams can investigate VM-to-VM, VM-to-pod, pod-to-pod and cross-cluster flows with Kubernetes-aware workload context using eBPF-enabled deep packet inspection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The ROI Case
&lt;/h3&gt;

&lt;p&gt;The core mechanism is consolidation onto a single management plane covering network connectivity, observability, and network policy. Instead of maintaining separate stacks and separate expertise for VMs and for containers, the teams responsible for availability, performance, and security each focus on one plane, with one consistent set of tools, telemetry, and controls. Additionally, the new architecture prevents vendor lock-in by Kubernetes platform vendors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost relief
&lt;/h3&gt;

&lt;p&gt;The immediate driver for most organizations is reducing the costs of legacy hypervisors. Every VM that moves off legacy infrastructure onto a converged Kubernetes platform is a licensing spend that stops compounding — budget that can be redirected to AI infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational efficiency
&lt;/h3&gt;

&lt;p&gt;Running one policy model, one routing and observability stack, and one set of operational runbooks for both VMs and containers means fewer specialized teams, less tooling overlap, and lower mean time to resolution (MTR) when something goes wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI readiness
&lt;/h3&gt;

&lt;p&gt;A converged platform is also the platform production AI workloads need. Self-hosted LLMs and the agents built on them can run alongside existing VM and container workloads, close to the data they need, with the performance, latency, and scale that production AI demands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The market is converging on one self-hosted platform for containers and VMs, and the economics and AI trends driving it are only accelerating. The AI-driven need for a converged, self-hosted platform is the reason to move to Kubernetes specifically, rather than to another hypervisor. Tigera already secures workloads across more than a million clusters for organizations including NVIDIA, Royal Bank of Canada, Bloomberg, Chipotle, GoDaddy, and Upwork.&lt;/p&gt;

&lt;p&gt;Migrate first. Modernize later. On your timeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ Learn more:&lt;/strong&gt; Visit the &lt;a href="https://www.tigera.io/tigera-products/vm-migration/" rel="noopener noreferrer"&gt;VM Migration page&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;→ See it in action:&lt;/strong&gt; View the &lt;a href="https://app.arcade.software/share/Pa1kvHOZzkXI43jXoHs2" rel="noopener noreferrer"&gt;self-paced overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ready to see Calico for VMs on Kubernetes in action? &lt;a href="https://app.arcade.software/share/Pa1kvHOZzkXI43jXoHs2" rel="noopener noreferrer"&gt;Walk through the live demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.tigera.io/blog/migrate-first-modernize-later-a-leadership-guide-to-converging-vms-and-containers-to-run-ai-workloads/" rel="noopener noreferrer"&gt;Migrate First, Modernize Later: A Leadership Guide to Converging VMs and Containers to Run AI Workloads&lt;/a&gt; appeared first on &lt;a href="https://www.tigera.io" rel="noopener noreferrer"&gt;Tigera – Creator of Calico&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>featuredblog</category>
      <category>technicalblog</category>
      <category>vmmigration</category>
      <category>products</category>
    </item>
    <item>
      <title>NVIDIA OpenShell Secures the Agent. Who Governs the Fleet?</title>
      <dc:creator>Alister Baroi</dc:creator>
      <pubDate>Wed, 15 Jul 2026 14:41:56 +0000</pubDate>
      <link>https://dev.to/tigeraio/nvidia-openshell-secures-the-agent-who-governs-the-fleet-3658</link>
      <guid>https://dev.to/tigeraio/nvidia-openshell-secures-the-agent-who-governs-the-fleet-3658</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F62ijoovqxaclrafa3tbv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F62ijoovqxaclrafa3tbv.png" alt="NVIDIA OpenShell" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most attempts to control AI agents work at the model layer (alignment, system prompts) or the application layer (guardrail libraries, output filters). Both share a flaw: the thing being secured is also the thing doing the securing. A sufficiently confused or sufficiently compromised agent can talk its way past its own instructions.&lt;/p&gt;

&lt;p&gt;OpenShell takes a different position, and it is the right one. Put the controls in the environment, where the agent cannot negotiate with them. An agent inside an OpenShell sandbox cannot leak a credential it never received, and cannot call an endpoint the kernel refuses to route.&lt;/p&gt;

&lt;p&gt;If that argument sounds familiar, it should. It is the same case we made in &lt;a href="https://www.tigera.io/blog/why-we-built-lynx-bringing-control-to-the-age-of-ai-agents/" rel="noopener noreferrer"&gt;Why We Built Lynx&lt;/a&gt; and throughout the &lt;a href="https://www.tigera.io/blog/the-ai-agent-accountability-crisis-why-governance-isnt-keeping-up-with-deployment/" rel="noopener noreferrer"&gt;AI agent accountability series&lt;/a&gt;: controls the agent can override are not controls. NVIDIA arriving at the same conclusion, with an Apache 2.0 project and a partner list that includes Cisco, CrowdStrike, Google Cloud, and Microsoft Security, is the strongest endorsement the environment-layer approach has had yet.&lt;/p&gt;

&lt;p&gt;So this is not a “versus” post. OpenShell and Lynx solve different halves of the same problem, and NVIDIA said so first: its own &lt;a href="https://blogs.nvidia.com/blog/secure-autonomous-ai-agents-openshell/" rel="noopener noreferrer"&gt;launch announcement&lt;/a&gt; says securing autonomous systems “requires an integrated ecosystem”.&lt;/p&gt;

&lt;h2&gt;
  
  
  What OpenShell actually does
&lt;/h2&gt;

&lt;p&gt;OpenShell is a secure runtime for a single agent on a single machine. You install it with one command, then launch an agent inside a sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openshell sandbox create &lt;span class="nt"&gt;--&lt;/span&gt; claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That agent (Claude Code, Codex, Cursor, OpenCode, or your own container image) now runs inside an isolated environment governed by a declarative YAML policy with four layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Filesystem:&lt;/strong&gt; Which paths the agent can read or write, enforced with Landlock and locked at sandbox creation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process:&lt;/strong&gt; Which binaries can execute and which syscalls are available, enforced with seccomp. An agent can install a verified skill but cannot run an unreviewed binary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network:&lt;/strong&gt; deny-by-default egress, intercepted at the HTTP method and path level, hot-reloadable as approvals are granted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inference:&lt;/strong&gt; A “Privacy Router” that decides which LLM backend serves each call, keeping sensitive context on local models and routing to frontier models only when policy allows. Credentials are swapped at the router, so the real API key never sits inside the sandbox.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The threat model is specific and well chosen: long-running, self-evolving agents with shell access, live credentials, and the ability to rewrite their own code. Prompt injection, malicious third-party skills, subagents inheriting permissions they should not have. When the agent hits a policy wall, it can propose a policy change and a human approves or rejects it. Autonomy with a human holding the pen.&lt;/p&gt;

&lt;p&gt;It is currently alpha (“proof of life,” &lt;a href="https://github.com/NVIDIA/OpenShell/blob/main/README.md#prerequisites" rel="noopener noreferrer"&gt;in NVIDIA’s words&lt;/a&gt;), runs on macOS, Windows via WSL 2, and Linux, and targets everything from a developer laptop to DGX-class machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where OpenShell stops, on purpose
&lt;/h2&gt;

&lt;p&gt;Here is the part that matters for anyone running agents in production. NVIDIA’s technical documentation is explicit about what OpenShell does not address:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent-to-agent communication governance&lt;/li&gt;
&lt;li&gt;Agent identity and authentication&lt;/li&gt;
&lt;li&gt;Cross-sandbox communication patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kubernetes is the near-miss on that list. OpenShell &lt;a href="https://docs.nvidia.com/openshell/latest/kubernetes/setup" rel="noopener noreferrer"&gt;does run on Kubernetes&lt;/a&gt;: an experimental Helm chart, marked not for production, that provisions sandbox pods on a cluster. But putting sandboxes &lt;em&gt;on&lt;/em&gt; Kubernetes and governing a fleet &lt;em&gt;across&lt;/em&gt; Kubernetes are different jobs. Each sandbox still enforces its own YAML in isolation, with no shared agent identity and no view of its neighbors.&lt;/p&gt;

&lt;p&gt;Read that list again. It is not a gap NVIDIA missed; it is a boundary they drew deliberately, and they drew it exactly where the fleet begins. OpenShell answers “what can this agent do on this box?” It does not attempt to answer “which of my two hundred agents called the payments MCP server last Tuesday, under whose authority, and using which model?” And as we argued in &lt;a href="https://www.tigera.io/blog/the-ai-agent-accountability-gap-why-network-policies-api-gateways-and-rbac-are-not-enough/" rel="noopener noreferrer"&gt;The AI Agent Accountability Gap&lt;/a&gt;, network policies, API gateways, and RBAC cannot answer those questions either.&lt;/p&gt;

&lt;p&gt;They are the questions Lynx exists for. Side by side:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Concern&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;OpenShell&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Lynx&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scope&lt;/td&gt;
&lt;td&gt;One agent, one sandbox&lt;/td&gt;
&lt;td&gt;A fleet of agents across a cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent identity &amp;amp; authentication&lt;/td&gt;
&lt;td&gt;No first-class agent identity (users and components authenticate; agents just get injected credentials)&lt;/td&gt;
&lt;td&gt;SPIFFE/SPIRE workload identity, mTLS, per-agent JWTs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy&lt;/td&gt;
&lt;td&gt;YAML per sandbox&lt;/td&gt;
&lt;td&gt;Cedar policy across agents, MCP servers, and LLM providers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A2A and MCP traffic&lt;/td&gt;
&lt;td&gt;Out of scope&lt;/td&gt;
&lt;td&gt;Gateway proxy, every request authorized individually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agents you didn’t launch&lt;/td&gt;
&lt;td&gt;Not applicable&lt;/td&gt;
&lt;td&gt;eBPF detection classifies them as sanctioned, shadow, or unknown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit&lt;/td&gt;
&lt;td&gt;Local allow/deny logs per sandbox&lt;/td&gt;
&lt;td&gt;Fleet-wide Agent Trail, including which model actually served each call&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One box, two hundred boxes. Same philosophy, different altitude.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing the gap: three integration patterns
&lt;/h2&gt;

&lt;p&gt;None of these require code changes in either product. They use configuration surfaces both systems document today: OpenShell’s deny-by-default egress policy and credential injection on one side, Lynx’s gateway, registry, and token service on the other. To be clear about what this is: a proposed reference architecture drawn from published documentation, not a tested walkthrough. OpenShell is weeks old and still alpha. But the seams line up well enough that I think the patterns are worth writing down now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 1: One road out of the sandbox
&lt;/h3&gt;

&lt;p&gt;OpenShell intercepts all outbound traffic and denies by default. So write the narrowest useful network policy: the only egress a sandbox is allowed is the Lynx Agent Gateway.&lt;/p&gt;

&lt;p&gt;Every MCP call, every &lt;a href="https://www.tigera.io/blog/how-ai-agents-communicate-understanding-the-a2a-protocol-for-kubernetes/" rel="noopener noreferrer"&gt;A2A request&lt;/a&gt;, every LLM call now has exactly one path, and that path runs through Cedar authorization on a per-request basis, with the decision recorded in Agent Trail. The division of labor is clean. OpenShell guarantees the agent &lt;em&gt;cannot go around&lt;/em&gt; the gateway, even if it is compromised and actively trying. Lynx decides &lt;em&gt;what is allowed through&lt;/em&gt; the gateway, and remembers what happened.&lt;/p&gt;

&lt;p&gt;Neither system can do the other’s job here. Lynx cannot stop a process inside someone’s laptop sandbox from opening a raw connection; OpenShell can. OpenShell has no idea whether this agent should be allowed to call that MCP tool with those arguments, but Lynx does.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 2: The API key never enters the sandbox
&lt;/h3&gt;

&lt;p&gt;OpenShell’s Privacy Router already routes inference calls through controlled backends and swaps credentials on the way out. Lynx, as of the current release cycle, treats LLM providers as first-class governed entities: registered in the registry, subject to Cedar policy, visible on the access map, recorded in Agent Trail down to the model that actually served the request.&lt;/p&gt;

&lt;p&gt;Chain them. Local-model traffic stays on the box, served by Nemotron or whatever the Privacy Router prefers. Frontier-model traffic routes to the Lynx LLM gateway, where Cedar decides which agent may use which provider and which model, and the credential is attached centrally.&lt;/p&gt;

&lt;p&gt;Follow the key. The OpenAI or Anthropic API key exists in exactly one place, inside Lynx. Not in the sandbox, not in the agent’s environment variables, not in a dotfile the agent can read and exfiltrate. And every frontier call, from every sandbox on every developer machine, lands in one audit trail with the caller’s identity and the served model attached. A prompt-injected agent can ask for the key all it wants; there is nothing on the box to steal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 3: Identity from birth
&lt;/h3&gt;

&lt;p&gt;OpenShell deliberately focuses on securely running agents rather than defining who those agents are. It provides sandboxing, credential management, and integration with existing identity systems, but it doesn’t maintain a persistent registry of agent identities or establish a trust model between agents. Lynx complements that layer by giving every agent a verifiable identity from the moment it is created.&lt;/p&gt;

&lt;p&gt;The integration is intentionally lightweight: a wrapper around &lt;code&gt;openshell sandbox create&lt;/code&gt; registers the new agent with the Lynx registry and associates it with an existing workload identity; whether SPIFFE, OIDC, or another supported mechanism. From its first network request, the sandbox represents a known, authenticated agent rather than an anonymous process.&lt;/p&gt;

&lt;p&gt;This pattern is what makes the first two enforceable &lt;em&gt;per agent&lt;/em&gt; instead of per box, and it has a side effect worth naming. A developer’s local experiment, sandboxed with OpenShell and registered with Lynx, shows up on your access map as a sanctioned agent. The same experiment without registration is exactly the shadow agent that &lt;a href="https://www.tigera.io/blog/a-field-guide-to-the-agents-in-your-cluster/" rel="noopener noreferrer"&gt;Lynx’s eBPF detection&lt;/a&gt; was built to catch. Registration at sandbox creation makes the sanctioned path the lazy path, which is the only kind of security policy developers reliably follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same policy idea, from laptop to cluster
&lt;/h2&gt;

&lt;p&gt;There is a deeper symmetry underneath these patterns. OpenShell’s filesystem and process layers do at sandbox scope roughly what Lynx’s agent-detector does at node scope with eBPF; its network and inference layers do locally what the Lynx gateway does for the fleet with Cedar. (Peter Kelly covered the gateway-plus-kernel enforcement model in &lt;a href="https://www.tigera.io/blog/multi-layer-policy-for-securing-ai-agents/" rel="noopener noreferrer"&gt;Multi-Layer Policy for Securing AI Agents&lt;/a&gt;.) Nobody has built a translator between OpenShell YAML and Cedar yet. But the layers correspond closely enough that policy parity across the laptop-to-cluster boundary looks like an engineering problem, not a research problem. An agent developed under a given OpenShell policy could be promoted to Kubernetes with the same intent expressed as Cedar plus a quarantine baseline. That is the roadmap conversation this post is meant to start.&lt;/p&gt;

&lt;p&gt;Two smaller threads point the same direction. OpenShell’s Kubernetes chart means sandboxes can run on a Lynx-governed cluster, sitting inside two independent kernel enforcement planes, one inside the sandbox and one on the node, so even a sandbox escape lands in Lynx’s detection perimeter. And OpenShell logs every allow/deny decision locally; forwarding those over OTLP into Agent Trail would put runtime decisions and traffic decisions in a single timeline. Both are speculative today. Neither is far-fetched.&lt;/p&gt;

&lt;h2&gt;
  
  
  The other half
&lt;/h2&gt;

&lt;p&gt;OpenShell is the most credible answer yet to a question we have been asking all year: how do you give an agent real autonomy without handing it the keys to the host? If you are running coding agents locally, try it; the install is two commands and the defaults are sensible.&lt;/p&gt;

&lt;p&gt;Then ask the question NVIDIA deliberately left open. When that agent, and the forty like it across your organization, start talking to MCP servers, to each other, and to three different LLM providers, who is checking identity at the door? Whose policy decides, and where is the record? (&lt;a href="https://www.tigera.io/blog/five-principles-of-an-accountable-ai-agent-network-how-to-evaluate-any-governance-platform/" rel="noopener noreferrer"&gt;Five Principles of an Accountable AI Agent Network&lt;/a&gt; is the checklist for evaluating whatever answers you get.)&lt;/p&gt;

&lt;p&gt;OpenShell holds the agent. Lynx governs the fleet. The seam between them is thinner than you would expect, and the patterns above are how we would stitch it.&lt;/p&gt;

&lt;p&gt;_Lynx is Tigera’s security and governance platform for AI agents on Kubernetes: identity, policy, detection, and audit for every agent in your cluster. Read &lt;a href="https://www.tigera.io/blog/how-lynx-works-a-technical-walkthrough/" rel="noopener noreferrer"&gt;How Lynx Works&lt;/a&gt; or schedule a demo at &lt;a href="https://www.tigera.io/demo/" rel="noopener noreferrer"&gt;tigera.io/demo/&lt;/a&gt;. _&lt;/p&gt;

&lt;p&gt;Ready to see Lynx in action? &lt;a href="https://www.tigera.io/demo/?product=lynx" rel="noopener noreferrer"&gt;Schedule a demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.tigera.io/blog/nvidia-openshell-secures-the-agent-who-governs-the-fleet/" rel="noopener noreferrer"&gt;NVIDIA OpenShell Secures the Agent. Who Governs the Fleet?&lt;/a&gt; appeared first on &lt;a href="https://www.tigera.io" rel="noopener noreferrer"&gt;Tigera – Creator of Calico&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>technicalblog</category>
      <category>aiagentsecurity</category>
      <category>products</category>
    </item>
    <item>
      <title>Tiered Network Policy: Scaling Kubernetes Security</title>
      <dc:creator>Alister Baroi</dc:creator>
      <pubDate>Fri, 10 Jul 2026 16:12:22 +0000</pubDate>
      <link>https://dev.to/tigeraio/tiered-network-policy-scaling-kubernetes-security-2bek</link>
      <guid>https://dev.to/tigeraio/tiered-network-policy-scaling-kubernetes-security-2bek</guid>
      <description>&lt;p&gt;As Kubernetes clusters scale from a few development sandboxes to massive, multi-tenant production environments, platform teams often find themselves facing a configuration management crisis. A small number of microservices suddenly demand hundreds of individual Kubernetes NetworkPolicy objects. Managing them becomes operationally expensive, auditing them is difficult, and a single developer misconfiguration can easily drop critical production traffic or open a massive security hole.&lt;/p&gt;

&lt;p&gt;To scale cluster security without slowing down engineering velocity, we must abandon the flat, uncoordinated rule planes of the past. The solution lies in establishing a clear, multi-layered framework: a hierarchy of trust powered by tiered network policies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Problem with Standard Kubernetes NetworkPolicy
&lt;/h2&gt;

&lt;p&gt;Standard Kubernetes NetworkPolicy resources are genuinely useful for basic application microsegmentation, but they have major architectural and organizational bottlenecks when scaled across an enterprise:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Namespace-Scoped by Design:&lt;/strong&gt; Standard network policies are inherently scoped to a namespace. If your security team mandates a cluster-wide rule, such as blocking all internal pods from querying the cloud provider’s metadata API (169.254.169.254), you have to copy-paste that policy into every single namespace. If a developer creates a new namespace, that guardrail doesn’t exist until someone manually applies it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organizational Friction:&lt;/strong&gt; Because anyone with namespace access can manipulate these policies, it creates a persona gap within organizations. Platform &amp;amp; Security teams need to enforce global, un-overrideable guardrails (e.g. “Isolate the payments namespace from everything else”). DevOps teams need the freedom to write granular, service-to-service rules for their applications without opening infrastructure support tickets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Rules Hierarchy:&lt;/strong&gt; Kubernetes network policies are strictly additive. There are no weights, priorities, or order sequences. An application developer can accidentally (or intentionally) write a loose policy that bypasses the security team’s intended restrictions, undermining any baseline trust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The “Allow-Only” Restriction:&lt;/strong&gt; Standard policies cannot explicitly Deny traffic. They operate solely on an allow-list model. Isolation is implicit: if a pod is selected by a policy, any traffic not explicitly allow-listed is dropped. This makes it impossible to write a simple, top-level rule that says, “Block traffic from Namespace X to Namespace Y, no matter what.”&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What a Scalable Solution Requires
&lt;/h2&gt;

&lt;p&gt;To solve these scaling pain points, we have to move away from a flat network architecture and adopt a Tiered Policy Model. A scalable solution requires four core capabilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Global, Cluster-Wide Scope:&lt;/strong&gt; To stop copy-pasting rules, administrators need a policy type that natively operates at the cluster level rather than the namespace level. This allows a single manifest to apply to all current and future namespaces automatically, eliminating the risk of “configuration drift” and ensuring day-one protection for new workloads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separation of Concerns (RBAC-Gated Tiers):&lt;/strong&gt; Security, platform, and application teams need their own distinct logical “zones” or tiers to deploy rules. These tiers must be strictly gated by Role-Based Access Control (RBAC) so a developer modifying their application namespace cannot alter or override a higher-priority platform or security tier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic, Top-Down Evaluation:&lt;/strong&gt; The firewall engine must evaluate these tiers sequentially. Traffic must pass through the highest-priority tier (e.g., Security) before it ever reaches a lower tier (e.g., Application).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit Deny and Pass Actions:&lt;/strong&gt; Standard policies are allow-only, so they can never express a hard “block this, period.” A tiered model needs explicit actions: a Deny that states a prohibition outright, and a third option, Pass, that lets one tier defer the decision to the next rather than ending it (covered in detail below).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why the Pass Action Matters
&lt;/h3&gt;

&lt;p&gt;The key enabler of tiered policies is the Pass action. Think of Pass as a delegated hand-off. When a packet matches a rule with a Pass action in a high-priority tier, the engine skips the remaining lower-precedence rules in that tier and continues evaluation in the next tier down the hierarchy. This allows security administrators to say: “This traffic is safe by our standards, but we aren’t explicitly endorsing it. We are passing the final decision down to the platform or development teams to handle at their layer.” Without a Pass action, tiered policies become brittle, forcing admins to explicitly track and approve every single microservice connection at the highest level, which would defeat the purpose of developer agility.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Kubernetes Native Answer: ClusterNetworkPolicy
&lt;/h2&gt;

&lt;p&gt;Recognizing these scalability constraints, the SIG-Network Policy API group developed a native, multi-layered solution: ClusterNetworkPolicy. The API delivers exactly the four capabilities outlined above, with a few concrete specifics worth calling out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A Native Three-Layer Hierarchy:&lt;/strong&gt; It introduces distinct, sequentially evaluated resource tiers. ClusterNetworkPolicy (Admin tier) at the top for absolute guardrails, standard NetworkPolicy in the middle for developer agility, and ClusterNetworkPolicy (Baseline tier) at the bottom as a cluster-wide fallback safety net. Unlike namespace-jailed standard policies, the Admin and Baseline tiers apply across the entire cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separation of Concerns:&lt;/strong&gt; Because ClusterNetworkPolicy is delivered as a new Custom Resource Definition (CRD) rather than a tweak to the existing NetworkPolicy type, standard Kubernetes RBAC governs who can interact with it. This ensures that only Security/Platform teams access ClusterNetworkPolicy resources, while DevOps teams work only with namespaced network policies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Numeric Precedence:&lt;/strong&gt; Policies feature explicit integer priorities. A policy with a lower integer value (e.g., 10) takes precedence over a policy with a higher value (e.g., 100), allowing for deterministic evaluation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit Actions:&lt;/strong&gt; Rules are no longer purely additive. You can now design rules with explicit Accept, Deny, and Pass actions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This API completely shifts how cluster administrators manage traffic by introducing a native, three-tiered evaluation hierarchy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft3mjswyeggd4ksy4lt0t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft3mjswyeggd4ksy4lt0t.png" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Top Layer: ClusterNetworkPolicy (Admin tier):&lt;/strong&gt; This is the high-priority tier controlled by cluster and security administrators. Rules here are evaluated first, and two of its three actions are terminal: an Accept or a Deny is a final verdict that bypasses the developer’s NetworkPolicy layer entirely. A Deny here cannot be overridden by any developer manifest, but the same is true of Accept: if an admin explicitly accepts traffic, it is permitted regardless of what a developer policy would have decided. This is the crucial difference from a standard NetworkPolicy allow, which is additive. An Admin-tier Accept is an override, not a contribution. Only the third action, Pass, is non-terminal: it declines to decide and hands evaluation down to the next tier.&lt;/p&gt;

&lt;p&gt;As an example, the following ClusterNetworkPolicy can be used to allow DNS UDP traffic toward kube-dns from all namespaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;policy.networking.k8s.io/v1alpha2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterNetworkPolicy&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow-dns-to-kube-dns&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Admin&lt;/span&gt;
  &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
  &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;namespaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
  &lt;span class="na"&gt;egress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow-dns&lt;/span&gt;
      &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Accept&lt;/span&gt;
      &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;namespaceSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;kubernetes.io/metadata.name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kube-system&lt;/span&gt;
            &lt;span class="na"&gt;podSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;k8s-app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kube-dns&lt;/span&gt;
      &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;udp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;destinationPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;53&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Middle Layer: Standard NetworkPolicy:&lt;/strong&gt; This is the traditional application-developer tier. It only kicks in if traffic wasn’t explicitly allowed or denied by the ClusterNetworkPolicy in the Admin tier above it. This keeps developers agile, letting them connect their microservices without needing admin intervention. One subtlety to keep in mind: standard NetworkPolicy carries an implicit deny for any pod it selects. So traffic only falls through to the Baseline tier when no NetworkPolicy selects the workload at all. A pod that is selected but matches none of its Accept rules is already dropped here, and never reaches the Baseline tier below. The following network policy can be used to permit ingress HTTP traffic for the awesome-app namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NetworkPolicy&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow-http-ingress&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;awesome-app&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;podSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http-server&lt;/span&gt;
  &lt;span class="na"&gt;policyTypes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
  &lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
         &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Bottom Layer: ClusterNetworkPolicy (Baseline tier):&lt;/strong&gt; This is the cluster-scoped Baseline tier, meant for default fallbacks. It acts as the safety net after developer policies are checked. For example, if a developer forgets to secure their pod, this policy can enforce a default cluster-wide posture like “if no developer policy matches this traffic, deny all intra-cluster traffic by default.”. The following ClusterNetworkPolicy would satisfy this requirement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;policy.networking.k8s.io/v1alpha2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterNetworkPolicy&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deny-all&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Baseline&lt;/span&gt;
  &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;namespaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
  &lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deny-all-ingress&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deny&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;namespaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Combined, these features provide a native, multi-level strategy for scaling enterprise cluster security far beyond the limitations of a flat configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending the Model: Calico Tiers
&lt;/h2&gt;

&lt;p&gt;While the native Kubernetes APIs introduce a better three-layer model, and some control over rule priority, enterprise environments often require finer granularity. Calico expands on this concept by offering unlimited policy tiers, allowing you to design an arbitrary number of custom evaluation layers. Calico tiers will be discussed in the next post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://demo.arcade.software/YOs9QHi74UXzkITd2n59" rel="noopener noreferrer"&gt;Get started with an interactive demo: DNS Policy with Calico&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.tigera.io/blog/tiered-network-policy-scaling-kubernetes-security/" rel="noopener noreferrer"&gt;Tiered Network Policy: Scaling Kubernetes Security&lt;/a&gt; appeared first on &lt;a href="https://www.tigera.io" rel="noopener noreferrer"&gt;Tigera – Creator of Calico&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>technicalblog</category>
      <category>bestpractices</category>
    </item>
    <item>
      <title>Save the Address, Save the Cloud: A Hands-on KubeVirt Live Migration Workshop</title>
      <dc:creator>Alister Baroi</dc:creator>
      <pubDate>Thu, 09 Jul 2026 13:58:26 +0000</pubDate>
      <link>https://dev.to/tigeraio/save-the-address-save-the-cloud-a-hands-on-kubevirt-live-migration-workshop-2lh0</link>
      <guid>https://dev.to/tigeraio/save-the-address-save-the-cloud-a-hands-on-kubevirt-live-migration-workshop-2lh0</guid>
      <description>&lt;p&gt;In the &lt;a href="https://www.tigera.io/blog/save-the-address-save-the-cloud-kubevirt-vm-migration-story/" rel="noopener noreferrer"&gt;previous post in this series&lt;/a&gt;, we covered why Virtual Machine (VM) Live Migration in Kubernetes is difficult: a VM’s IP is its identity, and the “new” VM on the destination node has to come up with the same IP, this something that Kubernetes is not known for, and on top of that, traffic has to switch over only after network security policies are in place. Calico v3.32.0 delivers all the above and allows you to Live Migrate a VM without any network disruptions and this post is a short, do-it-yourself workshop to achieve it.&lt;/p&gt;

&lt;p&gt;In about 5 minutes you’ll bring up a 3-node cluster, install Calico + KubeVirt, run a VM, and migrate it live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A Linux or a Windows Machine preferably WSL2 ( &lt;strong&gt;Mac Is not supported by KubeVirt&lt;/strong&gt; )&lt;/li&gt;
&lt;li&gt;Docker or Podman with at least 8 GB RAM&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/tools/#kubectl" rel="noopener noreferrer"&gt;kubectl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kind.sigs.k8s.io/docs/user/quick-start/#installation" rel="noopener noreferrer"&gt;KIND (v0.31.0)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubevirt.io/user-guide/user_workloads/virtctl_client_tool/" rel="noopener noreferrer"&gt;virtctl (v1.8.2)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In many Linux distros the default for most kernel parameters are too low, for a kind cluster running &lt;a href="https://www.tigera.io/learn/guides/kubevirt/" rel="noopener noreferrer"&gt;KubeVirt&lt;/a&gt;. Use the following command to temporarily increase these limits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; fs.inotify.max_user_instances&lt;span class="o"&gt;=&lt;/span&gt;2048
&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; fs.inotify.max_user_watches&lt;span class="o"&gt;=&lt;/span&gt;1048576
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you face any challenges during the &lt;a href="https://www.tigera.io/learn/guides/kubevirt/kubevirt-live-migration/" rel="noopener noreferrer"&gt;KubeVirt live migration&lt;/a&gt;, make sure to drop by &lt;a href="http://slack.projectcalico.org" rel="noopener noreferrer"&gt;our Slack&lt;/a&gt; to ask your questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a multi-node cluster
&lt;/h2&gt;

&lt;p&gt;By default KIND is shipped with a simple default CNI, use the following command to disable the default CNI and create the demo cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;kind create cluster --config -&amp;lt;&amp;lt;EOF&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cluster&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kind.x-k8s.io/v1alpha4&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;calico-lab&lt;/span&gt;
&lt;span class="na"&gt;nodes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;control-plane&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt;
&lt;span class="na"&gt;networking&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;disableDefaultCNI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;podSubnet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.0.0/16&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install Calico
&lt;/h2&gt;

&lt;p&gt;Live local &lt;a href="https://www.tigera.io/learn/guides/vmware-migration/" rel="noopener noreferrer"&gt;VM migration&lt;/a&gt; is part of Calico v3.32.0 release and it’s important that you install or upgrade to this specific version. If you are already running Calico Unified Platform in your environment skip this part and go directly to the “Version and feature verifications” step there you can check your version of Calico.&lt;/p&gt;

&lt;p&gt;Use the following command to install Tigera Operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/projectcalico/calico/v3.32.0/manifests/tigera-operator.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for the rollout to complete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; tigera-operator rollout status deploy/tigera-operator &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create the installation resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;kubectl create -f - &amp;lt;&amp;lt;'EOF'&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;operator.tigera.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Installation&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;kubeletVolumePluginPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;None&lt;/span&gt;
  &lt;span class="na"&gt;calicoNetwork&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;bgp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enabled&lt;/span&gt;
    &lt;span class="na"&gt;ipPools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;blockSize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;26&lt;/span&gt;
      &lt;span class="na"&gt;cidr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.0.0/16&lt;/span&gt;
      &lt;span class="na"&gt;encapsulation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IPIP&lt;/span&gt;
      &lt;span class="na"&gt;natOutgoing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enabled&lt;/span&gt;
      &lt;span class="na"&gt;nodeSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;all()&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;operator.tigera.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;APIServer&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for Calico installation to finish, you can verify that by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Available tigerastatus/calico &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install KubeVirt
&lt;/h2&gt;

&lt;p&gt;To extend Kubernetes to manage stateful virtual machines just like ordinary containers, you first need to install KubeVirt, which acts as the crucial abstraction layer between your cluster and the underlying QEMU emulator.&lt;/p&gt;

&lt;p&gt;Use the following command to install KubeVirt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/kubevirt/kubevirt/releases/download/v1.8.2/kubevirt-operator.yaml
kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/kubevirt/kubevirt/releases/download/v1.8.2/kubevirt-cr.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; kubevirt rollout status deploy/virt-operator &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Preparing KubeVirt
&lt;/h2&gt;

&lt;p&gt;To prepare the cluster for live migration, we must first configure KubeVirt to enable bridge networking on the pod network. This is the only networking mode that allows Calico to successfully persist a VM’s IP address across nodes. The permitBridgeInterfaceOnPodNetwork flag is a cluster-wide configuration in KubeVirt that determines whether a Virtual Machine (VM) can utilize the bridge interface type for its default pod network. While this is often set to true by default, cluster administrators sometimes disable it (set it to false) for security or architectural reasons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;kubectl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;kubevirt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;patch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;kubevirt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;kubevirt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;--type=merge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"spec"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"configuration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"developerConfiguration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"useEmulation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"clusterProfiler"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"defaultNetworkInterface"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bridge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"permitBridgeInterfaceOnPodNetwork"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After configuration is in place KubeVirt will spawn handler and API pods, this can take some time depending on your machine.&lt;/p&gt;

&lt;p&gt;Use the following command to make sure KubeVirt deployment is complete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; kubevirt &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Available kubevirt/kubevirt &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a VM
&lt;/h2&gt;

&lt;p&gt;Two things make this VM migratable: &lt;code&gt;bridge: {}&lt;/code&gt; networking, and the &lt;code&gt;allow-pod-bridge-network-live-migration&lt;/code&gt; annotation (KubeVirt blocks bridge-mode migration without it).&lt;/p&gt;

&lt;p&gt;Use the following command to create a VM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/frozenprocess/kubevirt-migration-observer/main/examples/vm.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Live VM Migration
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.tigera.io/learn/guides/vmware-migration/vmware-live-migration/" rel="noopener noreferrer"&gt;Live VM migration&lt;/a&gt; is a marathon relay, there are multiple KubeVirt and Calico components that work together in order to make this migration happen and the beauty of this integration is that all the complexity is hidden behind a single command &lt;code&gt;virtctl migrate&lt;/code&gt;. While Calico Unified Platform is heavily involved in the security and networking side of a VM migration process, KubeVirt handles the compute lifecycle, specifically racing the guest’s memory across the wire and cutting the CPU over to the new node.&lt;/p&gt;

&lt;p&gt;To better understand this dance let’s use the KubeVirt &lt;a href="https://github.com/frozenprocess/kubevirt-migration-observer/tree/main" rel="noopener noreferrer"&gt;observer app&lt;/a&gt;, this app will gather all the information regarding your cluster during the migration and organize it in a searchable way.&lt;/p&gt;

&lt;p&gt;Use the following command to deploy the observer app inside the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/frozenprocess/kubevirt-migration-observer/main/examples/observer-job.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After observer is running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;virtctl migrate demo-vm 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gathering The Report
&lt;/h2&gt;

&lt;p&gt;To make sure that the report is generated use the following command to take a peak at the observer status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl logs &lt;span class="nt"&gt;-l&lt;/span&gt; job-name&lt;span class="o"&gt;=&lt;/span&gt;kubevirt-migration-observer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expected result should be the following:&lt;br&gt;&lt;br&gt;
report written:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;report written&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;markdown&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/work/reports/demo-vm-20260604T232645Z.md&lt;/span&gt;
  &lt;span class="na"&gt;json &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/work/reports/demo-vm-20260604T232645Z.json&lt;/span&gt;
  &lt;span class="na"&gt;html &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/work/reports/demo-vm-20260604T232645Z.html&lt;/span&gt;
  &lt;span class="na"&gt;audit &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/work/reports/demo-vm-20260604T232645Z-audit/audit.md&lt;/span&gt;
&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;observer&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt; &lt;span class="s"&gt;report written to /work/reports; holding 3600s for kubectl cp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the following command to copy the report to your workstation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;pod&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; default get pod &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kubevirt-migration-observer &lt;span class="nt"&gt;-o&lt;/span&gt; name&lt;span class="si"&gt;)&lt;/span&gt;
kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; default &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;#pod/&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:/work/reports"&lt;/span&gt; ./reports
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now head over to the reports folder on your local machine where you executed the command and examine the report.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flzldsql9bwhd2sqaf10m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flzldsql9bwhd2sqaf10m.png" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; observer app also has the ability to collect performance logs, and flamegraphs. If you are interested in running a full VM migration profile checkout the full tutorial &lt;a href="https://github.com/frozenprocess/kubevirt-migration-observer" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following table compares two independent migration reports:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;data plane Configuration&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Cutover VM Downtime (Via a TCP Probe)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Total Migration Time&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BGP + IP-in-IP&lt;/td&gt;
&lt;td&gt;0s (None observed)&lt;/td&gt;
&lt;td&gt;1m 13.7s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VXLAN + BGP&lt;/td&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;1m 44.5s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Clean up
&lt;/h2&gt;

&lt;p&gt;Run the following command to delete the demo environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kind delete clusters calico-lab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Three resources do all the heavy lifting: the &lt;code&gt;kubeVirtVMAddressPersistence&lt;/code&gt; setting on Calico’s IPAM config, the &lt;code&gt;allow-pod-bridge-network-live-migration&lt;/code&gt; annotation on the VM, and bridge-mode networking so the VM uses the pod IP directly. Get those right and a stateful VM moves between machines with its TCP connections open and its identity intact. The observer just makes the proof visible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://calico-vm-demo-ui.calico-labs.com/" rel="noopener noreferrer"&gt;Try Calico VM migration in your browser&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.tigera.io/blog/save-the-address-save-the-cloud-a-hands-on-kubevirt-live-migration-workshop/" rel="noopener noreferrer"&gt;Save the Address, Save the Cloud: A Hands-on KubeVirt Live Migration Workshop&lt;/a&gt; appeared first on &lt;a href="https://www.tigera.io" rel="noopener noreferrer"&gt;Tigera – Creator of Calico&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>technicalblog</category>
      <category>vmmigration</category>
      <category>howto</category>
    </item>
    <item>
      <title>Save the Address, Save the Cloud (KubeVirt VM Migration Story)</title>
      <dc:creator>Alister Baroi</dc:creator>
      <pubDate>Wed, 08 Jul 2026 20:45:58 +0000</pubDate>
      <link>https://dev.to/tigeraio/save-the-address-save-the-cloud-kubevirt-vm-migration-story-1p04</link>
      <guid>https://dev.to/tigeraio/save-the-address-save-the-cloud-kubevirt-vm-migration-story-1p04</guid>
      <description>&lt;p&gt;Kubernetes is built for containers, and it’s been doing that since it used to run docker as an engine for its containers. But what if you want to add VMs to the mix? After all, containers are ephemeral and don’t require fixed IPs as they shift the identity toward labels, but VMs on the other hand are tied to IP addresses and in some cases MAC addresses.&lt;/p&gt;

&lt;p&gt;This brings us to this blog about VM migration and IP preservation. Unlike a pod that can be part of a deployment and run in a swarm of stateless endpoints, a VM is a stateful machine run by hypervisor like QEMU and extended to Kubernetes via KubeVirt Custom Resource Definitions (CRDs).&lt;/p&gt;

&lt;h2&gt;
  
  
  There Is Something About KubeVirt
&lt;/h2&gt;

&lt;p&gt;KubeVirt is an abstraction layer between the underlying hypervisor (QEMU) on your machine and Kubernetes. Its job is to manage a VM’s lifecycle and provide the necessary requirements for a VM to be a native resident in Kubernetes. These requirements are CPU, Memory, Networking, etc.&lt;/p&gt;

&lt;p&gt;KubeVirt does this by wrapping each VM in an ordinary Kubernetes pod called &lt;code&gt;virt-launcher&lt;/code&gt;. Inside that pod, KubeVirt runs &lt;code&gt;libvirt&lt;/code&gt; and QEMU, and the “VM” is really just a process scheduled, networked, and accounted for like any other pod. That detail matters a lot once we get to migration: when a VM moves to another node, what Kubernetes actually does is create a brand-new &lt;code&gt;virt-launcher&lt;/code&gt; pod on the destination and tear down the old one. Everything hard about live migration comes from making that pod swap invisible to the workload running inside.&lt;/p&gt;

&lt;h3&gt;
  
  
  CPU
&lt;/h3&gt;

&lt;p&gt;CPU is the part that does the actual work, every instruction the guest operating system and its applications execute runs on a virtual CPU that KubeVirt maps onto real cores of the host node. You can pin the VM to dedicated cores, expose host CPU features, or let it float over shared cores. For migration, the CPU matters for a subtle reason: while a VM is being moved, its CPU keeps running and keeps changing memory. The faster the guest dirties memory, the harder it is to copy that memory to the other node before it changes again. We’ll come back to this race in a moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory
&lt;/h3&gt;

&lt;p&gt;Other than being expensive these days, RAM or Memory has a crucial role in VM migration, since it is the place where everything that the CPU is working on is stored and referenced. In a physical computer, memory is the expensive stick that you buy and install in your computer. However, in a VM, memory is a region of your computer’s RAM allocated for the tasks that the VM is actively working on.&lt;/p&gt;

&lt;p&gt;Memory is the thing migration is really about. When KubeVirt live-migrates a VM, the bulk of what it ships from the source node to the destination node is the VM’s RAM, gigabytes of it, while the guest keeps running and keeps writing to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Networking
&lt;/h3&gt;

&lt;p&gt;Another important part of a VM is networking, and KubeVirt supports multiple networking modes. Our focus is going to be on bridge, since that is required for VM migration with Calico, but if you’d like to learn more about other modes feel free to check out the official &lt;a href="https://kubevirt.io/user-guide/network/interfaces_and_networks/" rel="noopener noreferrer"&gt;KubeVirt documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  What is a bridge?
&lt;/h4&gt;

&lt;p&gt;A bridge is similar to a playground where all the resources connected to it are able to communicate with each other. In Linux, a software bridge is a virtual switch: you plug interfaces into it and it forwards Ethernet frames between them just like a physical switch would.&lt;/p&gt;

&lt;p&gt;In KubeVirt’s &lt;code&gt;bridge&lt;/code&gt; mode, the VM is connected to the pod network through a Linux bridge, and the pod’s IP address is handed down to the VM itself (via DHCP). The guest doesn’t get some separate, NAT’d address, it uses the pod IP directly as its own. If the relevant pod interface has a MAC address and the VM doesn’t override it, the VM inherits that MAC too.&lt;/p&gt;

&lt;p&gt;That “VM uses the pod IP directly” property is exactly why bridge mode is the only mode that works for live migration with Calico, and it’s the hinge the rest of this post turns on.&lt;/p&gt;

&lt;h2&gt;
  
  
  How local live migration actually works
&lt;/h2&gt;

&lt;p&gt;Local live migration is the process of moving a running VM from one node to another within a cluster while the guest keeps running and stays reachable. No reboot, no shutdown, ideally the application inside never even notices.&lt;/p&gt;

&lt;p&gt;You start the process by posting a &lt;code&gt;VirtualMachineInstanceMigration&lt;/code&gt; (VMIM) object, or just running &lt;code&gt;virtctl migrate vm1&lt;/code&gt;, and KubeVirt does the rest. The default strategy is pre-copy, and it works roughly like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A new target VM (a fresh &lt;code&gt;virt-launcher&lt;/code&gt; pod) is created on the destination node, while the source machine is still running.&lt;/li&gt;
&lt;li&gt;The source starts streaming chunks of VM state, mostly RAM, to the target. This repeats: pages that the guest dirties while the copy is in flight get re-sent.&lt;/li&gt;
&lt;li&gt;Once enough state has transferred that only a tiny delta remains, the guest is briefly paused, the last pages are shipped, and the guest resumes on the target.&lt;/li&gt;
&lt;li&gt;The source VM is removed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keep in mind that the VM migration is a race between the source and your network speed. If the source is copying memory to the target and the guest is simultaneously adding new things to that memory and this rate is faster than the network can copy them, the migration may never converge. KubeVirt has knobs for that (auto-converge throttles the guest CPU; post-copy runs the guest on the target immediately and faults memory across on demand), but for most workloads pre-copy just works, and that’s what we’ll see in the real report below.&lt;/p&gt;

&lt;p&gt;KubeVirt is deliberately conservative here. Out of the box it runs at most 5 migrations in parallel cluster-wide, no more than 2 outbound per node, and caps each migration at 64 MiB/s of bandwidth, so a busy cluster doesn’t saturate its own network moving VMs around.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard part: the VM has to keep its IP
&lt;/h2&gt;

&lt;p&gt;Here’s the thing the KubeVirt docs gloss over and where Calico Unified Platform does the heavy lifting. Remember that a migration is really a pod swap, old &lt;code&gt;virt-launcher&lt;/code&gt; pod on node A dies, and a new one on node B is born. Normally, a brand-new pod means a brand-new IP. For a stateless web pod behind a Service, who cares. For a VM, the IP is its identity, every open TCP connection, every client that resolves it, every firewall rule references that address. Change the IP and you’ve effectively killed and rebooted the machine, which is the exact opposite of “live.”&lt;/p&gt;

&lt;p&gt;So the job is: the new pod on node B must come up with the same IP the old pod had on node A, traffic must switch over to node B at exactly the right moment, and network policy has to already be in place on node B before that switch, otherwise the VM lands on the new node and gets firewalled off from its own connections. Calico coordinates all of this. Here’s how the pieces fit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bridge mode is non-negotiable
&lt;/h2&gt;

&lt;p&gt;Because Calico’s IP persistence depends on the VM IP matching the pod IP, only bridge mode qualifies. Modes like &lt;code&gt;masquerade&lt;/code&gt; give the VM a different internal IP than the pod and NAT between them, which breaks both IP persistence and policy enforcement during migration. KubeVirt actually refuses to migrate a bridge-mode VM unless you opt in with an annotation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubevirt.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VirtualMachine&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vm&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;kubevirt.io/allow-pod-bridge-network-live-migration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  VM IP address persistence
&lt;/h2&gt;

&lt;p&gt;Calico keys its IP allocation off the VM’s identity rather than the pod’s. Internally, instead of the usual per-pod IPAM handle, the address is held under a VM-scoped handle like &lt;code&gt;k8s-pod-network.vmi.default.vm1&lt;/code&gt;. When the target pod is created during migration, Calico’s CNI plugin recognizes it as a KubeVirt &lt;code&gt;virt-launcher&lt;/code&gt; pod, looks up that VM handle, finds the existing IP, and reuses it instead of allocating a fresh one.&lt;/p&gt;

&lt;p&gt;This is a cluster-wide setting added in Calico v3.32 (&lt;code&gt;kubeVirtVMAddressPersistence&lt;/code&gt;, enabled by default), and it’s mandatory for this type of migration. With persistence off, Calico rejects the migration target outright.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t NAT the VM on the way out
&lt;/h2&gt;

&lt;p&gt;If a VM talks to something outside the cluster and &lt;code&gt;natOutgoing&lt;/code&gt; is enabled on its IP pool, the server on the other end sees the VM’s traffic as coming from the node’s IP. Migrate the VM and that source IP changes from node A’s address to node B’s, and any in-flight connection to that external server breaks. So for migratable VMs you disable &lt;code&gt;natOutgoing&lt;/code&gt; on their pool, keeping the VM’s own IP on the wire end-to-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Switching the traffic with BGP route priority
&lt;/h2&gt;

&lt;p&gt;Both nodes briefly believe they host the VM’s &lt;code&gt;/32&lt;/code&gt; route. Calico breaks the tie with route priority: the target host programs the route with an elevated priority (a lower kernel metric, 512 by default) than the source’s normal priority (1024). Lower metric wins in the Linux kernel, so traffic steers to the target. Within a single AS this propagates automatically (Calico maps the kernel metric onto BGP &lt;code&gt;local_pref&lt;/code&gt;); across eBGP rack boundaries you carry the signal with BGP communities via a &lt;code&gt;BGPFilter&lt;/code&gt;. After a convergence window (~30s by default), priorities return to normal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Policy must land before the switch
&lt;/h2&gt;

&lt;p&gt;This is the subtle one. If the VM activated on the target node before its network policy was programmed there, it would arrive into a node that doesn’t yet know its firewall rules, and get cut off. Calico prevents this with an interlock: when the CNI plugin sets up the migration target pod, it returns the IP with empty routes, deliberately not programming the host-side routes that would pull traffic over. Felix only completes that switch once policy is in place on the destination (governed by &lt;code&gt;policy_setup_timeout_seconds&lt;/code&gt;). Policy first, traffic second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Live migration looks like magic, a running machine teleports between hosts and nobody notices, but it’s really two systems cooperating very carefully. KubeVirt handles the compute side: racing the guest’s memory across the wire with pre-copy until it converges, then cutting the CPU over in under a second. Calico handles the network side: pinning the VM’s IP to the VM’s identity so the new pod reclaims it, withholding routes until policy is programmed on the destination, switching traffic with BGP route priority, and cleaning up dual ownership after a convergence window, all without the VM’s address ever changing.&lt;/p&gt;

&lt;p&gt;Get the networking mode wrong (anything but bridge), forget to disable &lt;code&gt;natOutgoing&lt;/code&gt;, or skip the policy interlock, and “live” migration becomes a reboot with extra steps. Get them right, as the report above shows, and a stateful VM moves between physical machines while its TCP connections stay open and its identity stays put.&lt;/p&gt;

&lt;p&gt;Watch an interactive demo: &lt;a href="https://app.arcade.software/share/YBtRqEnDgzll8GyFUDrh" rel="noopener noreferrer"&gt;Live Migration of VMs Running on Kubernetes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.tigera.io/blog/save-the-address-save-the-cloud-kubevirt-vm-migration-story/" rel="noopener noreferrer"&gt;Save the Address, Save the Cloud (KubeVirt VM Migration Story)&lt;/a&gt; appeared first on &lt;a href="https://www.tigera.io" rel="noopener noreferrer"&gt;Tigera – Creator of Calico&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>technicalblog</category>
      <category>vmmigration</category>
      <category>products</category>
    </item>
    <item>
      <title>Six AI agent SDKs for enterprise Kubernetes, compared</title>
      <dc:creator>Alister Baroi</dc:creator>
      <pubDate>Fri, 03 Jul 2026 20:44:49 +0000</pubDate>
      <link>https://dev.to/tigeraio/six-ai-agent-sdks-for-enterprise-kubernetes-compared-433j</link>
      <guid>https://dev.to/tigeraio/six-ai-agent-sdks-for-enterprise-kubernetes-compared-433j</guid>
      <description>&lt;p&gt;There’s a question we hear constantly from platform and engineering leaders right now, &lt;strong&gt;&lt;em&gt;“which agent SDK should we standardize on for our Kubernetes clusters?”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The honest answer is that the question is slightly wrong, and the rest of this post explains why. But it’s a fair question, so let’s compare the contenders first.&lt;/p&gt;

&lt;p&gt;If you’re an enterprise running on-premise or in your own VPC, the SDK you pick has to do two things most of the _ &lt;strong&gt;“build an agent in 20 lines”&lt;/strong&gt; _ tutorials skip over. It has to run in a container you control, and it has to talk to a model you can host yourself. That second one rules out a surprising amount.&lt;/p&gt;

&lt;h2&gt;
  
  
  The six SDKs most people are actually using
&lt;/h2&gt;

&lt;p&gt;These are the ones with the most mindshare in mid-2026. There are others, but these are the names that come up in every conversation. They sit on a rough spectrum of model freedom: most will happily run against a model you host yourself, the OpenAI SDK will too but treats that as a side path, and one of them (Anthropic’s) is tied to a single vendor’s models. I’ve ordered them with the most flexible first.&lt;/p&gt;

&lt;h3&gt;
  
  
  LangGraph
&lt;/h3&gt;

&lt;p&gt;LangChain’s lower-level library. You model your agent as a directed graph: nodes do work, edges decide what happens next, and the whole thing checkpoints its state so a long-running agent can pause, resume, and even rewind. If your problem is _ &lt;strong&gt;“this workflow is genuinely complicated and has to survive restarts,”&lt;/strong&gt; _ LangGraph is the one built for that.&lt;/p&gt;

&lt;p&gt;For on-prem it’s reasonable. There’s a &lt;a href="https://docs.langchain.com/langsmith/deploy-standalone-server" rel="noopener noreferrer"&gt;Helm chart for self-hosting&lt;/a&gt;, the &lt;a href="https://github.com/langchain-ai/langgraph" rel="noopener noreferrer"&gt;core is MIT-licensed&lt;/a&gt;, and it’s model-agnostic so you can point it at a local model. The catch is operational weight: a production self-hosted deployment wants Postgres for state and Redis for streaming, so you’re running real infrastructure, not just a pod. The platform layer on top is commercial.&lt;/p&gt;

&lt;h3&gt;
  
  
  CrewAI
&lt;/h3&gt;

&lt;p&gt;The one your team will get running fastest. You describe a &lt;em&gt;“crew”&lt;/em&gt; of agents with roles (&lt;em&gt;“researcher”, “writer”&lt;/em&gt;) and let them collaborate. The learning curve is the lowest of the six, the &lt;a href="https://github.com/crewAIInc/crewAI" rel="noopener noreferrer"&gt;core is open source and MIT-licensed&lt;/a&gt;, built from scratch without a LangChain dependency, and it’s genuinely model-agnostic. People wire it up to Ollama or a self-hosted vLLM endpoint without much fuss. There’s a &lt;a href="https://enterprise-docs.crewai.com/configuration/configuration" rel="noopener noreferrer"&gt;Helm chart for the enterprise platform&lt;/a&gt;, and if you want it to feel native to Kubernetes you can wrap crews as custom resources with something like &lt;a href="https://kagent.dev/" rel="noopener noreferrer"&gt;Kagent&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The tradeoff for that simplicity is control. When you want fine-grained say over exactly what happens at each step, the role-based abstraction can feel like it’s deciding things for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Google ADK
&lt;/h3&gt;

&lt;p&gt;Google’s &lt;a href="https://adk.dev/" rel="noopener noreferrer"&gt;ADK&lt;/a&gt;. The model here is a hierarchy: a root agent delegates to sub-agents, and it speaks the A2A (agent-to-agent) protocol natively, so agents built in different frameworks can talk to each other. It’s Apache 2.0 and &lt;a href="https://adk.dev/" rel="noopener noreferrer"&gt;ships in Python, TypeScript, Go, Java, and Kotlin&lt;/a&gt;, with the Python implementation the oldest and most complete. Its own docs say it “&lt;a href="https://adk.dev/" rel="noopener noreferrer"&gt;can work with almost any generative AI model&lt;/a&gt;,” with documented support for Claude, Ollama, vLLM, and others through LiteLLM, so it’s genuinely model-agnostic despite the Gemini-first defaults.&lt;/p&gt;

&lt;p&gt;It looks Google-Cloud-coupled, and it does have a one-command adk deploy gke path, but that’s a convenience, not a requirement. Underneath it’s a container. You can run it on any on-prem cluster with hand-written manifests, and Google has published a &lt;a href="https://docs.cloud.google.com/kubernetes-engine/docs/tutorials/agentic-adk-vllm" rel="noopener noreferrer"&gt;real reference for running ADK against a self-hosted Llama model on vLLM&lt;/a&gt;. It’s Gemini-first by default, but you can bring other models through LiteLLM. Less locked-in than the branding suggests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Microsoft Agent Framework
&lt;/h3&gt;

&lt;p&gt;The grown-up merger of two earlier projects: AutoGen, which is where a lot of the multi-agent research came from, and Semantic Kernel, which is where the enterprise plumbing lived. It runs on Python and .NET, which is the real reason it’s on this list. If you’re a Microsoft and .NET shop, this is the one that speaks your language, literally.&lt;/p&gt;

&lt;p&gt;It does two kinds of orchestration: the loose, LLM-driven kind where agents reason their way through a problem, and the deterministic, business-logic kind where you want a workflow to run the same way every time. For on-prem it’s a good citizen. It’s &lt;a href="https://github.com/microsoft/agent-framework" rel="noopener noreferrer"&gt;MIT-licensed and open source&lt;/a&gt;, it’s &lt;a href="https://learn.microsoft.com/en-us/agent-framework/agents/providers/" rel="noopener noreferrer"&gt;genuinely model-agnostic&lt;/a&gt; with first-party connectors that include Ollama, and people are already running it on AKS or plain Kubernetes against local open-weight models like Qwen or Mistral. One thing to keep straight: Microsoft’s hosted agent service is an Azure product, but the framework itself is yours to run wherever.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenAI Agents SDK
&lt;/h3&gt;

&lt;p&gt;The cleanest developer experience of the six. It ships in Python and TypeScript, agents hand off control to each other explicitly, the API is small, and if your team already uses the OpenAI API they’ll be productive in an afternoon. For self-hosting, you bring your own container and infrastructure, which is fine.&lt;/p&gt;

&lt;p&gt;It’s also more model-flexible than the name suggests, and this is the part worth knowing because it’s easy to miss. The API guide on the OpenAI platform site barely mentions it, but the Agents SDK’s own documentation has a &lt;a href="https://openai.github.io/openai-agents-python/models/" rel="noopener noreferrer"&gt;“Models” page&lt;/a&gt; that points you to non-OpenAI providers two ways. The clean one is any OpenAI-compatible endpoint: you set a base URL and an API key, which covers local models served through vLLM or Ollama. Beyond that, official LiteLLM and Any-LLM extensions reach 100-plus providers, though the docs flag those as best-effort and beta. So you can run it fully self-hosted against your own model. OpenAI is still the default and best-supported path, but the lock-in is softer than the name implies. The next entry is where the real model lock-in lives.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anthropic Claude Agent SDK
&lt;/h3&gt;

&lt;p&gt;Anthropic’s harness, and the same engine that powers Claude Code, &lt;a href="https://code.claude.com/docs/en/agent-sdk/overview" rel="noopener noreferrer"&gt;exposed as a library in Python and TypeScript&lt;/a&gt;. It spawns and supervises a CLI subprocess that owns a shell and a working directory, which is a genuinely different model from the others. Every agent is a long-lived process with state on disk, so you think about it more like running a fleet of little workers than calling a stateless API. The &lt;a href="https://github.com/anthropics/claude-agent-sdk-python" rel="noopener noreferrer"&gt;SDK code is MIT-licensed&lt;/a&gt;, though Anthropic’s docs note that use of it is governed by their &lt;a href="https://code.claude.com/docs/en/agent-sdk/overview" rel="noopener noreferrer"&gt;Commercial Terms of Service&lt;/a&gt;, and Anthropic ships Dockerfiles and Kubernetes manifests for self-hosting it.&lt;/p&gt;

&lt;p&gt;The honest caveat is the model. This runs on Claude, full stop, and it’s the only one of the six with no supported way to swap in your own model. You can route through &lt;em&gt;Amazon Bedrock, Google’s Gemini Enterprise Agent Platform&lt;/em&gt; (&lt;a href="https://docs.cloud.google.com/gemini-enterprise-agent-platform/vertex-ai-name-changes" rel="noopener noreferrer"&gt;formerly Vertex AI&lt;/a&gt;), or Azure to keep traffic inside a cloud account you control, which helps with compliance, but those are all just channels for hosting Claude, not alternative model vendors. There’s no air-gapped, weights-on-your-own-GPU story the way there is with the open-weight crowd. If your on-prem requirement is about latency, data residency, or “our cloud, our keys,” it can work. If it’s about never sending a token off the box, it can’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  The comparison at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;SDK&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Languages&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;License&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Weaknesses&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Ideal use case&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LangGraph&lt;/td&gt;
&lt;td&gt;Python, JS/TS&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;Durable checkpointed state, pause/resume/rewind, model-agnostic&lt;/td&gt;
&lt;td&gt;Operationally heavy (Postgres + Redis), commercial platform tier, steeper mental model&lt;/td&gt;
&lt;td&gt;Complex, long-running workflows that must survive restarts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CrewAI&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;Fastest to ship, lowest learning curve, model-agnostic, K8s-native via KAgent&lt;/td&gt;
&lt;td&gt;Less fine-grained step control, the role abstraction can over-decide&lt;/td&gt;
&lt;td&gt;Rapid multi-agent collaboration, prototype to production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google ADK&lt;/td&gt;
&lt;td&gt;Python, TS, Go, Java, Kotlin&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;Native A2A, hierarchical delegation, broad language support, works with almost any model via LiteLLM&lt;/td&gt;
&lt;td&gt;Gemini-first defaults, branding implies GCP lock-in&lt;/td&gt;
&lt;td&gt;Multi-framework systems betting on agent-to-agent interop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft Agent Framework&lt;/td&gt;
&lt;td&gt;Python, .NET (C#)&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;Creative plus deterministic orchestration, first-party connectors including Ollama, reached 1.0&lt;/td&gt;
&lt;td&gt;Youngest of the group, hosted agent service is Azure-only&lt;/td&gt;
&lt;td&gt;C#/.NET teams needing both orchestration styles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI Agents SDK&lt;/td&gt;
&lt;td&gt;Python, JS/TS&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;Cleanest developer experience, small API, runs local models via OpenAI-compatible endpoints (LiteLLM/Any-LLM as beta options)&lt;/td&gt;
&lt;td&gt;OpenAI is the default and best-supported path; broad provider routing is beta&lt;/td&gt;
&lt;td&gt;Teams who want speed, lean on OpenAI, but want an escape hatch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anthropic Claude Agent SDK&lt;/td&gt;
&lt;td&gt;Python, TS&lt;/td&gt;
&lt;td&gt;MIT code; use under Anthropic Commercial ToS&lt;/td&gt;
&lt;td&gt;Claude Code engine as a library, ships Docker and K8s manifests&lt;/td&gt;
&lt;td&gt;Claude-only (no non-Anthropic or local models), stateful subprocess hosting model&lt;/td&gt;
&lt;td&gt;Claude-centric teams comfortable routing via Bedrock or Gemini Enterprise&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Picking one (or, realistically, several)
&lt;/h2&gt;

&lt;p&gt;LangGraph if the workflow is hard and stateful. CrewAI if you want multi-agent collaboration running by Friday. ADK if you’re betting on A2A and a mix of frameworks talking to each other. Microsoft Agent Framework if your stack is already C#/.NET, or you want both creative and deterministic orchestration in one place. OpenAI’s SDK for the cleanest developer experience, with non-OpenAI and local models available through OpenAI-compatible endpoints (or its beta LiteLLM extension) if you need them. Claude’s Agent SDK if you want the Claude Code engine as a library and Bedrock or Gemini Enterprise is close enough to “on-prem” for you.&lt;/p&gt;

&lt;p&gt;Five of the six can run against a model you host yourself. Four treat that as a first-class path, the OpenAI SDK does it through OpenAI-compatible endpoints (with LiteLLM as a beta add-on), and only Anthropic’s Claude Agent SDK is locked to a single vendor’s models, though Bedrock or Gemini Enterprise at least keep that traffic in your own cloud. For an on-premise enterprise that model-freedom question is the biggest filter. After that, the choice is mostly about how your team thinks: graphs, crews, hierarchies, or handoffs.&lt;/p&gt;

&lt;p&gt;The honest part, though, is that most enterprises don’t pick one. The data team gets something working in CrewAI in a day. A platform engineer builds the stateful pipeline in LangGraph because nothing else handles the checkpointing. The .NET team reaches for Microsoft’s framework. Someone ships a Claude or OpenAI SDK agent before anyone writes a standard down. A year later you’re running several of these at once, plus whatever lands next quarter. That’s not a failure of planning. It’s just what a healthy, fast-moving org looks like, and it’s worth designing for rather than fighting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Governing the fleet you’ll actually have
&lt;/h2&gt;

&lt;p&gt;Here’s the catch that sits underneath all six options. Once an agent is a running pod, the SDK that built it no longer matters. From the cluster’s point of view, every agent looks the same: a workload making network calls to a model and to tools, acting on behalf of someone, doing things you didn’t watch happen. The SDK’s view stops at the edge of its own process. Your security and platform teams’ problem doesn’t.&lt;/p&gt;

&lt;p&gt;None of the six frameworks govern that. It isn’t their job. They help a developer build an agent; they don’t tell you which agents exist in your cluster, what they’re allowed to reach, or what they actually did with the credentials you handed them. And because you’ll be running more than one framework, anything that only governs agents written a particular way leaves most of your fleet uncovered.&lt;/p&gt;

&lt;p&gt;This is the gap &lt;a href="https://www.tigera.io/tigera-products/lynx/" rel="noopener noreferrer"&gt;Tigera Lynx&lt;/a&gt; is built to close. It governs agents at the platform layer instead of inside any single SDK, so the same controls apply whether the agent was written in LangGraph, CrewAI, ADK, or something that doesn’t exist yet. Lynx discovers the agents already running, including the ones nobody registered, using eBPF down at the kernel where the network call happens. An agent that skips your gateway entirely still shows up, because a syscall is a syscall regardless of the framework above it.&lt;/p&gt;

&lt;p&gt;From there it puts a single control point in the path of every agent interaction and requires no changes to the agent’s code to do it. If governance depends on every developer importing your library and using it correctly, you don’t have governance, you have a polite request. Lynx works at the level where that assumption can’t break: discovery, policy, and a full audit trail your security team gets handed instead of reconstructing after the first incident. It’s already running in production at large banks, which are not known for a relaxed view of risk.&lt;/p&gt;

&lt;p&gt;Pick the SDK that fits how your team builds. The decision that actually carries risk is whether anything sits between your agents and the rest of your cluster once they’re live, and that layer has to be SDK-agnostic, because your fleet already is. If your teams are shipping agents faster than you can govern them, &lt;a href="https://www.tigera.io/tigera-products/lynx/" rel="noopener noreferrer"&gt;see how Lynx governs AI agents on Kubernetes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://app.arcade.software/share/otiiHhZXpIRhebuAZqOv" rel="noopener noreferrer"&gt;See Lynx discover and govern agents in a 3-minute interactive demo →&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.tigera.io/blog/six-ai-agent-sdks-for-enterprise-kubernetes-compared/" rel="noopener noreferrer"&gt;Six AI agent SDKs for enterprise Kubernetes, compared&lt;/a&gt; appeared first on &lt;a href="https://www.tigera.io" rel="noopener noreferrer"&gt;Tigera – Creator of Calico&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>technicalblog</category>
      <category>aiagentsecurity</category>
    </item>
  </channel>
</rss>
