AWS VPC CNI
Reference for the AWS VPC CNI TCP preservation path — autodiscovery, configuration, external connections, limitations, and comparison with Traffic Control.
For the TC alternative, see Traffic Control.
What the VPC-CNI-based path does
When a pod is live-migrated to a new node, its pod IP usually changes. Established connections to peers on other nodes would normally break because those peers still send traffic to the old IP.
The VPC-CNI-based path solves this by keeping the original pod IP after migration. The destination node assigns the migrated pod the same secondary IP that it had on the source node. Because the IP does not change, peers continue talking to the same address and no packet rewriting is needed.
This path is an alternative to the TC path. You choose one or the other, not both.
When to choose the VPC-CNI-based path
Choose this path when:
- You are running on AWS EKS with the AWS VPC CNI plugin installed.
- You need to preserve IPv4 traffic other than TCP — for example IPv4 UDP — because the real IPv4 pod IP stays unchanged and the path is IPv4 protocol-agnostic.
- You cannot run the privileged node-level TC DaemonSet.
- You are okay with a potentially longer freeze time during migration while AWS networking re-assigns the same IP.
Choose the TC path when:
- You want lower freeze times and only need IPv4 TCP preserved.
- You are not using the Cast AI-managed AWS VPC CNI fork for IP preservation.
- Your kernel supports eBPF TCX (6.6+) and you can run a privileged DaemonSet.
NoteThe VPC-CNI-based path can have a longer freeze time than the TC path because it waits for AWS networking to assign the same IP on the destination node. Unlike the TC path, it does not require a privileged DaemonSet. Because the pod IP is preserved, connections from resources in the same VPC but outside the cluster (e.g. RDS databases, EC2 instances) continue to work after migration — the TC path cannot preserve these because external resources do not run the TC daemon.
How it works
-
Autodiscovery. During installation the chart looks up the existing
aws-nodeDaemonSet inkube-system. If it exists, has anaws-nodecontainer, and does not use an unsupported configuration, the chart decides to install the managed VPC CNI fork. -
Separate the original CNI from live-migration nodes. The original
aws-nodeDaemonSet is mutated so it does not run on nodes labeledlive.cast.ai/install: "true". This is done both by a Helm pre-install/pre-upgrade Job and by a mutating admission webhook that watches changes to thekube-system/aws-nodeDaemonSet. -
Install the Cast AI-managed fork on live-migration nodes. The chart deploys a second
aws-nodeDaemonSet (namedcastai-aws-nodeby default) that uses the Cast AI fork image and a node affinity requiringlive.cast.ai/install: "true". It inherits key environment variables from the original DaemonSet so it uses the same VPC, subnet, and warm-pool settings. -
Preserve the IP during migration. When the destination pod is created, the managed VPC CNI fork assigns it the same secondary IP that the source pod had. Because the IP does not change, existing connections resume without packet rewriting.
-
Enable TCP preservation by default. When the VPC CNI path is active,
features.migrationOptions.default.withTCPdefaults totrue, so migrations automatically try to preserve established TCP sockets.
Configuring the chart
The VPC CNI path is controlled by the castai-aws-vpc-cni section in the chart values and by autodiscovery logic.
Enable or disable autodiscovery
castai-aws-vpc-cni:
enabled: true # allow autodiscovery/installation (default)
# enabled: false # unconditionally skip the VPC CNI pathSetting enabled: false disables the path entirely, regardless of whether aws-node is detected. You might do this when you want to use the TC path exclusively, or when you are running Calico as the full CNI and do not need the managed VPC CNI fork.
Autodiscovery rules
The chart installs the managed fork only when all of the following are true:
castai-aws-vpc-cni.enabledis not explicitlyfalse.- A DaemonSet named
aws-nodeexists inkube-system. - That DaemonSet has a container named
aws-node(confirming it is the AWS VPC CNI plugin). - The existing DaemonSet does not have any of these unsupported settings:
ENABLE_PREFIX_DELEGATION=trueENABLE_IPv6=trueENABLE_MULTI_NIC=true
If any unsupported setting is detected, the managed fork is not installed. The cluster then falls back to the TC path (if tc.enabled: true) or to no TCP preservation. This check runs at install time — after installation, the active path is determined by which components were successfully deployed (VPC CNI fork, TC DaemonSet, or neither).
Core values
| Value | Default | Description |
|---|---|---|
castai-aws-vpc-cni.enabled | true | Allow the chart to autodiscover and install the managed VPC CNI fork. Set to false to skip the path unconditionally. |
castai-aws-vpc-cni.fullnameOverride | castai-aws-node | Name of the managed DaemonSet deployed on live-migration nodes. |
castai-aws-vpc-cni.image.override | Defined in chart values.yaml | Cast AI fork image used on live-migration nodes. The exact tag is set by the chart and changes with each release. |
castai-aws-vpc-cni.envOverrides | {} | Map of environment variables that always override anything else on the managed fork, including autodiscovered values from kube-system/aws-node and values set under castai-aws-vpc-cni.env. Use this, not env, to force a value for any of the 8 autodiscovered keys. See Autodiscovered environment variables below. |
castai-aws-vpc-cni.affinity | live.cast.ai/install In ["true"] | Affinity that restricts the managed fork to live-migration nodes. |
The controller is informed about VPC CNI through command-line flags generated from the chart. When autodiscovery succeeds, the controller defaults withTCP to true.
Autodiscovered environment variables
The chart reads these 8 variables from the existing aws-node DaemonSet and merges them into the managed fork:
ENI_CONFIG_LABEL_DEFWARM_IP_TARGETAWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFGADDITIONAL_ENI_TAGSCLUSTER_ENDPOINTCLUSTER_NAMEVPC_IDENI_CONFIG_ANNOTATION_DEF
This ensures the Cast AI-managed fork uses the same VPC, subnets, custom network configuration, and warm-pool behavior as the original plugin.
Precedence (lowest to highest) for these 8 keys on the managed fork:
- Subchart defaults from
helm/dependencies/aws-vpc-cni/values.yaml. - Autodiscovered values read from the existing
kube-system/aws-nodeDaemonSet, which always override both the subchart default and anything set undercastai-aws-vpc-cni.env. castai-aws-vpc-cni.envOverrides.<KEY>, which always overrides any key, autodiscovered or not.
ImportantAutodiscovery overrides
castai-aws-vpc-cni.envin the precedence order. Settingcastai-aws-vpc-cni.env.AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFGorenv.ADDITIONAL_ENI_TAGSdoes not override what was autodiscovered fromaws-node. To force a value for any of the 8 keys, useenvOverrides. Two of the 8 autodiscovered keys,AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFGandADDITIONAL_ENI_TAGS, have non-empty subchart defaults.
Example (values file):
castai-aws-vpc-cni:
envOverrides:
AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG: "true"Equivalent --set form:
helm upgrade --reuse-values <release-name> <chart> \
--set castai-aws-vpc-cni.envOverrides.AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG=trueenvOverrides may also be used for any other env var, not only the 8 autodiscovered ones — it is the single override path for the managed fork's environment.
Mutation webhook and install hook
Two mechanisms keep the original aws-node DaemonSet off live-migration nodes:
- Pre-install / pre-upgrade Job: Patches the existing
kube-system/aws-nodeDaemonSet to add the node affinitylive.cast.ai/install NotIn ["true"]. - Mutating admission webhook: Watches for updates to
kube-system/aws-nodeand re-applies the same affinity if something removes it.
On uninstall, a pre-delete Job removes the affinity expression from the original DaemonSet.
Migration options
When the VPC CNI path is active, features.migrationOptions.default.withTCP defaults to true. You can override it explicitly if needed:
features:
migrationOptions:
default:
withTCP: trueApply without a values file:
helm upgrade --reuse-values <release-name> <chart> \
--set features.migrationOptions.default.withTCP=trueIf you manage configuration in a values file, use -f values.yaml instead of --set.
External connections
The VPC-CNI-based path keeps the pod's original IPv4 IP, so intra-VPC traffic and any connection that is truly bound to the pod IP can survive migration. However, by default AWS VPC CNI Source-NATs outbound traffic to destinations outside the VPC to the node IP. External connections therefore usually depend on the node IP, not only the pod IP.
Outbound connections to external endpoints
By default, AWS_VPC_K8S_CNI_EXTERNALSNAT is false in the chart. With this default, traffic from a pod to an IP address outside the VPC is SNATed to the primary private IP of the node's primary ENI. The external endpoint sees the node IP as the source, and the conntrack entry is tied to the original node.
Because the migrated pod runs on a different node, that SNAT/conntrack state is lost. The connection usually breaks even though the pod IP itself did not change.
If you set AWS_VPC_K8S_CNI_EXTERNALSNAT=true and route outbound traffic through an external NAT gateway (for example, an AWS NAT Gateway with a stable IP), the external endpoint sees the NAT gateway IP instead of the node IP. In that setup, the pod IP is preserved by CLM and the NAT gateway IP is stable, so outbound connections are more likely to survive.
Inbound connections from external endpoints
Inbound external traffic generally reaches the pod through one of these paths:
- Direct pod IP targeting (for example, a Network Load Balancer with pod IP targets, or direct intra-VPC traffic): can survive because the destination pod IP does not change.
- NodePort or Load Balancer targeting node IPs: usually breaks because the connection state is tied to the original node. When the pod moves, the node-level conntrack and routing state do not move with it.
Mitigation with ingress/egress proxies
If your workload needs long-lived external connections and you cannot rely on pod-IP-only routing, the usual mitigation is to terminate external connections in a proxy or load balancer that is independent of the migrating pod:
- Ingress: Put a layer-7 or layer-4 load balancer / reverse proxy in front of the pods. The external client talks to the proxy, and the proxy opens a separate connection to the pod. When the pod migrates, the proxy reconnects to the new instance.
- Egress: Send outbound traffic through an egress proxy or NAT gateway with a stable IP. The pod talks to the proxy, and the proxy opens the external connection. Migration only affects the pod-to-proxy hop, which can be re-established.
This is the same pattern used for any Kubernetes workload that needs stable external identities: keep the external identity on the proxy or gateway, not on the individual pod.
Limitations and real-world setup guidance
Incompatible VPC CNI configurations
The managed fork is not installed, and therefore the VPC-CNI-based path is unavailable, if the existing aws-node uses any of these settings:
| Setting | Why it is incompatible |
|---|---|
ENABLE_PREFIX_DELEGATION=true | Prefix delegation allocates /28 prefixes rather than individual secondary IPs. The current path requires individual secondary IP assignment. |
ENABLE_IPv6=true | Only IPv4 is supported by the managed fork path. |
ENABLE_MULTI_NIC=true | Multi-NIC mode is not supported. |
If any of these are enabled, use the TC path or disable TCP preservation.
Warm pool and small subnets
AWS VPC CNI maintains a warm pool of secondary IPs or prefixes on each node. The size of that pool is controlled by WARM_IP_TARGET, WARM_PREFIX_TARGET, and MINIMUM_IP_TARGET.
A common source of failures in small subnets looks like this:
- A subnet has only 32 available IPs.
MINIMUM_IP_TARGETis set to 16 andWARM_IP_TARGETis 2.- A node holds a full
/28prefix (16 IPs) to satisfy the warm target, even if it is only running a few pods. - When a pod is migrated to that node, VPC CNI may report that no IP is available because the warm pool has consumed the subnet, even though the node is not actually using all of those IPs.
This is especially problematic when:
- Subnets are small or highly fragmented.
- Many nodes are cordoned or draining but still hold warm IPs.
- The cluster has high pod churn.
Best practice: For small subnets, tune WARM_IP_TARGET and MINIMUM_IP_TARGET to values slightly above the expected pod density per node, rather than using the default WARM_ENI_TARGET=1 which allocates a full ENI worth of IPs. AWS documentation recommends MINIMUM_IP_TARGET slightly higher than the expected pod count and a small WARM_IP_TARGET.
See the AWS VPC CNI docs on prefix and IP targets and ENI and IP targets for details.
Same-VPC and same-subnet considerations
The VPC-CNI-based path works best when:
- Source and destination nodes are in the same VPC and can reach the same subnet.
- The destination node has an available secondary IP in the same subnet as the source pod.
- No custom networking configuration prevents IP reuse across availability zones.
Setups that can cause problems include:
- Multiple small subnets per AZ with custom networking. If
AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG=truemaps different subnets to different nodes or AZs, the destination node may not have an IP available in the source pod's subnet, and migration will fail. - Random ENI or subnet assignment. If the CNI can place the pod in any of several subnets, the destination pod may receive a different subnet and therefore cannot reuse the source IP.
- Security Groups per Pod. The managed fork does not enable
ENABLE_POD_ENI; if you rely on per-pod security groups, verify that behavior is compatible with your setup.
Best practice: Use a single, large subnet per AZ for live-migration nodes, avoid custom networking unless you can guarantee the same subnet on both source and destination, and ensure WARM_IP_TARGET/MINIMUM_IP_TARGET leave enough free IPs for migrations.
Freeze time
Because the migration waits for the AWS API to assign the same secondary IP on the destination node, the pod freeze time can be longer than with the TC path. The exact duration depends on:
- Whether the destination node already has a free secondary IP in its warm pool.
- Whether VPC CNI needs to attach a new ENI (can take up to ~10 seconds).
- EC2 API latency and throttling.
For a side-by-side comparison of the VPC CNI and Traffic Control paths across all dimensions, see the Cloud providers comparison table.
External references
- AWS EKS documentation: Prefix Mode for Linux and Amazon VPC CNI.
- amazon-vpc-cni-k8s project: prefix and IP targets, ENI and IP targets, and VPC CNI configuration variables.
See also
Updated 2 hours ago
