Calico How-to Guides¶
Scope
Task recipes for Calico Open Source 3.32 (v3.32.2): install, switch data planes, enable flow logs, set up the ingress gateway, encrypt traffic, peer with BGP, write policies, upgrade, and troubleshoot. Background is in Explanation; defaults, ports and keys are in Reference.
Version-specific commands
Commands below pin v3.32.2. From 3.32 the operator manifest no longer bundles the CRDs, so you apply v1_crd_projectcalico_org.yaml first. For 3.31 the CRD file is operator-crds.yaml. Use kubectl create (or kubectl apply --server-side) because the CRD bundle is too large for client-side kubectl apply.
Deployment¶
Install with the Tigera Operator (manifests)¶
This is the recommended path. The default custom-resources.yaml creates Installation, APIServer, Goldmane and Whisker, with a 192.168.0.0/16 pool using VXLANCrossSubnet.
# 1. Calico CRDs (3.32+), 2. operator, 3. custom resources
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/v1_crd_projectcalico_org.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/tigera-operator.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/custom-resources.yaml
# Wait until every component reports AVAILABLE=True
watch kubectl get tigerastatus
To change the pod CIDR or encapsulation, download custom-resources.yaml and edit the Installation before creating it:
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
calicoNetwork:
ipPools:
- name: default-ipv4-ippool
blockSize: 26
cidr: 10.244.0.0/16
encapsulation: VXLANCrossSubnet
natOutgoing: Enabled
nodeSelector: all()
Install with Helm¶
helm repo add projectcalico https://docs.tigera.io/calico/charts
kubectl create namespace tigera-operator
# CRDs ship in a companion chart since 3.32
helm template calico-crds projectcalico/crd.projectcalico.org.v1 --version v3.32.2 \
| kubectl apply --server-side -f -
helm install calico projectcalico/tigera-operator --version v3.32.2 --namespace tigera-operator
watch kubectl get pods -n calico-system
On EKS, GKE, AKS or MKE, pass a values.yaml that sets installation.kubernetesProvider (for example echo '{ installation: {kubernetesProvider: EKS }}' > values.yaml) and add -f values.yaml. Charts are also pushed to the OCI registry quay.io/calico/charts since 3.32.
Install with native v3 CRDs (tech preview)¶
Native v3 CRDs replace the aggregated calico-apiserver. They need the Kubernetes MutatingAdmissionPolicy API.
helm template calico-crds projectcalico/projectcalico.org.v3 --version v3.32.2 \
| kubectl apply --server-side -f -
helm install calico projectcalico/tigera-operator --version v3.32.2 --namespace tigera-operator
Existing clusters migrate with a DatastoreMigration resource named v1-to-v3 (CRD manifest manifests/migration.projectcalico.org_datastoremigrations.yaml since 3.32.2). Follow the official "Migrate from API server to native CRDs" guide, because the datastore is briefly locked during the migration.
Install calicoctl¶
curl -L https://github.com/projectcalico/calico/releases/download/v3.32.2/calicoctl-linux-amd64 -o calicoctl
chmod +x calicoctl && sudo mv calicoctl /usr/local/bin/
calicoctl version
Since 3.32.2 calicoctl is also packaged as .deb and .rpm in the Calico package repositories. With the API server or native v3 CRDs, kubectl can manage projectcalico.org/v3 resources directly.
Switch Data Planes¶
Enable eBPF mode (operator, automatic)¶
For kubeadm-style clusters whose kube-proxy is not managed by Helm or Argo CD, the operator configures API server access and disables kube-proxy itself (3.31+):
kubectl patch installation.operator.tigera.io default --type merge \
-p '{"spec":{"calicoNetwork":{"linuxDataplane":"BPF","bpfNetworkBootstrap":"Enabled","kubeProxyManagement":"Enabled"}}}'
For a new cluster, create custom-resources-bpf.yaml instead of custom-resources.yaml:
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/custom-resources-bpf.yaml
Node port disruption
Nodes switch one at a time, so traffic through NodePorts can be disrupted briefly. On kernels with BTF, VXLAN connections do not survive the switch, because Felix recreates vxlan.calico in flow mode.
Enable eBPF mode (manual path)¶
On other clusters, first point Calico at the API server directly (the kubernetes-services-endpoint ConfigMap in the operator namespace, per the official guide), then:
# Operator installs: switch the data plane
kubectl patch installation.operator.tigera.io default --type merge \
--patch='{"spec":{"calicoNetwork":{"linuxDataplane":"BPF"}}}'
# Manifest installs: set it on Felix instead
calicoctl patch felixconfiguration default --type='merge' -p '{"spec":{"bpfEnabled":true}}'
# Stop kube-proxy by giving it a node selector that matches no nodes
kubectl patch ds -n kube-system kube-proxy \
-p '{"spec":{"template":{"spec":{"nodeSelector":{"non-calico":"true"}}}}}'
For better external-traffic performance, turn on direct server return:
kubectl patch felixconfiguration default --type='merge' -p '{"spec":{"bpfExternalServiceMode":"DSR"}}'
Enable Maglev load balancing for a Service (eBPF, 3.32+)¶
Requires eBPF in DSR mode; applies to external traffic only (not pod-to-Service, NodePort or Services with External Traffic Policy set).
Use the nftables data plane¶
kube-proxy must also run in nftables mode (Kubernetes 1.31+; mode: nftables in KubeProxyConfiguration). Then set the data plane in the Installation:
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
calicoNetwork:
linuxDataplane: Nftables
ipPools:
- name: default-ipv4-ippool
cidr: 192.168.0.0/16
encapsulation: VXLANCrossSubnet
natOutgoing: Enabled
Enable Flow Logs (Goldmane and Whisker)¶
New 3.30+ installs include both. Clusters upgraded from 3.29 or earlier need the custom resources (operator or Helm installs only):
kubectl apply -f - <<EOF
apiVersion: operator.tigera.io/v1
kind: Goldmane
metadata:
name: default
---
apiVersion: operator.tigera.io/v1
kind: Whisker
metadata:
name: default
EOF
# Open the console at http://localhost:8081
kubectl port-forward -n calico-system service/whisker 8081:8081
Do not expose Whisker publicly without authentication. Calico installs policies that deny other ingress to it.
Set Up Calico Ingress Gateway¶
# Deploy Envoy Gateway and the tigera-gateway-class GatewayClass
kubectl apply -f - <<EOF
apiVersion: operator.tigera.io/v1
kind: GatewayAPI
metadata:
name: default
EOF
kubectl get gatewayclass
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: web
namespace: demo
spec:
gatewayClassName: tigera-gateway-class
listeners:
- name: http
protocol: HTTP
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web
namespace: demo
spec:
parentRefs:
- name: web
rules:
- backendRefs:
- name: frontend
port: 8080
Gateway API CRD versions
3.31.6+ and 3.32.1+ bundle Envoy Gateway 1.8, which needs Gateway API v1.5.1 CRDs. If the operator manages them (the default), set crdManagement: Reconcile on the GatewayAPI resource before upgrading; if you manage them yourself, install v1.5.1 first.
Encrypt Traffic with WireGuard¶
# IPv4 and IPv6 inter-node encryption
kubectl patch felixconfiguration default --type='merge' \
-p '{"spec":{"wireguardEnabled":true,"wireguardEnabledV6":true}}'
# Verify: each node publishes a WireGuard public key
calicoctl get node <node-name> -o yaml | grep -i wireguardPublicKey
# EKS/AKS with the cloud CNI only: also encrypt host-network traffic
calicoctl patch felixconfiguration default --type='merge' -p '{"spec":{"wireguardHostEncryptionEnabled":true}}'
Review the MTU afterwards; WireGuard adds overhead per packet.
BGP Peering¶
Peer a rack of nodes with a top-of-rack router:
apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
name: rack-peer
spec:
peerIP: 10.0.0.1
asNumber: 64512
nodeSelector: rack == 'rack1'
Above about 100 nodes, disable the full mesh and use route reflectors:
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
name: default
spec:
nodeToNodeMeshEnabled: false
asNumber: 64512
Since 3.31 a BGPPeer can set localASNumber to use a different local ASN for one external peer.
Network Policies¶
Default deny¶
# Calico GlobalNetworkPolicy: deny all ingress by default
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: default-deny
spec:
selector: all()
types:
- Ingress
Exclude system namespaces
selector: all() also selects kube-system and calico-system pods. In production use a selector such as projectcalico.org/namespace not in {'kube-system', 'calico-system'} or add explicit allow rules first.
Tiered allow policy¶
apiVersion: projectcalico.org/v3
kind: Tier
metadata:
name: application
spec:
order: 500
---
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: allow-frontend
namespace: default
spec:
tier: application
selector: app == 'backend'
ingress:
- action: Allow
source:
selector: app == 'frontend'
destination:
ports:
- 8080
Since 3.32 the policy name no longer needs the application. tier prefix.
Test a policy before enforcing it¶
Create the policy as StagedNetworkPolicy (same spec), watch its verdicts in Whisker, then apply it as NetworkPolicy.
Cluster-wide guardrail with ClusterNetworkPolicy (3.32+)¶
Use the upstream policy.networking.k8s.io/v1alpha2 ClusterNetworkPolicy with tier: Admin (cannot be overridden by namespaces) or tier: Baseline (default that namespaces can override). Remove any AdminNetworkPolicy or BaselineAdminNetworkPolicy objects before upgrading to 3.32, which no longer enforces them.
Upgrade Calico¶
Operator installs:
curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/v1_crd_projectcalico_org.yaml
curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/tigera-operator.yaml
kubectl apply --server-side --force-conflicts -f v1_crd_projectcalico_org.yaml
kubectl apply --server-side --force-conflicts -f tigera-operator.yaml
watch kubectl get tigerastatus
Helm installs: apply the new CRDs first (Helm does not upgrade CRDs), then upgrade the chart:
helm template calico-crds projectcalico/crd.projectcalico.org.v1 --version v3.32.2 \
| kubectl apply --server-side --force-conflicts -f -
helm upgrade calico projectcalico/tigera-operator --version v3.32.2 --namespace tigera-operator
Upgrade checklist for 3.32
Replace AdminNetworkPolicy/BaselineAdminNetworkPolicy with ClusterNetworkPolicy; update anything that references the allow-tigera tier (now calico-system); upgrade Gateway API CRDs to v1.5.1 if you use the ingress gateway; move Helm-managed CRDs to the companion CRD chart.
Common Issues¶
| Issue | Diagnosis | Fix |
|---|---|---|
| Pod connectivity fails | calicoctl node status, kubectl get tigerastatus |
Check Felix health, BGP sessions, IP pool encapsulation |
| Policy not applied | calicoctl get workloadendpoints -A |
Verify label selectors, tier order and Pass actions |
| Traffic unexpectedly denied | Whisker flow logs filtered by verdict deny |
Read the policy trace; add an allow rule or Pass in the right tier |
| IP exhaustion | calicoctl ipam show --show-blocks |
Add an IP pool or enlarge the CIDR; check for leaked IPs with calicoctl ipam check |
| eBPF conntrack map full | calico-node -bpf conntrack dump (JSON output since 3.32) |
Increase bpfMapSizeConntrack, or rely on bpfMapSizeConntrackScaling: DoubleIfFull |
| Interfaces unmanaged on the node | NetworkManager owns cali* / vxlan.calico |
Configure NetworkManager to ignore Calico interfaces |
| Ingress Gateway crash-loops on OpenShift or GKE | Missing ListenerSet, TLSRoute or BackendTLSPolicy CRDs |
Upgrade to 3.31.7 / 3.32.2 or install the missing CRDs |
Commands & Recipes¶
Diagnostics¶
# Node status and BGP peers
calicoctl node status
# Component health (operator installs)
kubectl get tigerastatus
# Policies, endpoints and pools
calicoctl get networkpolicy -A -o wide
calicoctl get globalnetworkpolicy -o yaml
calicoctl get workloadendpoint -A
calicoctl get ippool -o wide
# IPAM usage and consistency
calicoctl ipam show --show-blocks
calicoctl ipam check
# Collect a diagnostics bundle for the whole cluster
calicoctl cluster diags
eBPF inspection¶
# Run inside a calico-node pod in eBPF mode
kubectl exec -n calico-system ds/calico-node -c calico-node -- calico-node -bpf nat dump
kubectl exec -n calico-system ds/calico-node -c calico-node -- calico-node -bpf conntrack dump
kubectl exec -n calico-system ds/calico-node -c calico-node -- calico-node -bpf policy dump eth0 all
Goldmane API access¶
# Goldmane requires mTLS; fetch client credentials from calico-node's certs
kubectl get secret -n calico-system node-certs --template='{{index .data "tls.key"}}' | base64 -d > tls.key
kubectl get secret -n calico-system node-certs --template='{{index .data "tls.crt"}}' | base64 -d > tls.crt
kubectl get secret -n calico-system goldmane-key-pair --template='{{index .data "tls.crt"}}' | base64 -d > ca.crt
kubectl port-forward -n calico-system svc/goldmane 7443:7443