The Linkerd CNI plugin is a Kubernetes network plugin responsible for injecting the Linkerd proxy sidecar into your pods. Normally, this injection happens via an init container. This article explains how to install Linkerd without relying on this init container.

Let’s see this in action. Imagine a simple nginx deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

When Linkerd is installed with its default CNI, this nginx pod would be modified to include the Linkerd proxy. The linkerd-proxy container would be added, and an initContainer would run first to set up networking before the nginx container starts.

However, some environments, like certain Kubernetes distributions or sandboxed environments, might not allow or easily support init containers. This is where installing Linkerd’s CNI plugin without the init container becomes crucial.

The core problem Linkerd’s CNI plugin solves is transparently injecting the linkerd-proxy sidecar into your application pods. This allows Linkerd to intercept and manage all inbound and outbound network traffic for those pods, enabling features like mTLS, observability, and traffic routing without requiring any changes to your application code or Dockerfiles. The standard approach uses an init container to perform some setup steps before the main application container starts.

When you opt to install Linkerd’s CNI plugin without the init container, the CNI plugin itself takes on the responsibility for the necessary network namespace manipulations. Instead of an init container preparing the network environment, the CNI plugin, when Kubernetes schedules a pod, directly modifies the pod’s network configuration. This typically involves setting up network interfaces and routing rules within the pod’s network namespace to ensure traffic flows through the linkerd-proxy sidecar. The linkerd-proxy container is then added to the pod’s spec, and it starts alongside your application container, ready to handle traffic.

To achieve this, you’ll typically use the Linkerd CLI to install Linkerd, specifying the proxy-init component to be disabled. The command might look something like this:

linkerd install --crds | kubectl apply -f -
linkerd install --without proxy-init | kubectl apply -f -

This command sequence first applies the Custom Resource Definitions (CRDs) that Linkerd requires, and then proceeds with the main Linkerd installation, explicitly excluding the proxy-init component. The CNI plugin will then be configured to handle the necessary network setup directly.

The key difference from a networking perspective is that the CNI plugin itself manipulates the pod’s network namespace. It acts as the "first responder" to a new pod in its network domain. When a pod is created, the CNI plugin intercepts this event. It then configures the pod’s network interfaces, routing tables, and potentially firewall rules. After the network is set up by the CNI plugin, the linkerd-proxy container is added to the pod’s definition, and it’s started. This sidesteps the need for a separate initContainer to perform similar network-related setup tasks.

You can verify that the CNI plugin is running by checking the linkerd-proxy-injector pod in the linkerd namespace. It should be running and not be part of a multi-container pod that also includes a proxy-init container. You can also inspect a newly created application pod (after it’s been injected) to confirm the absence of the linkerd-proxy-init container.

kubectl get pods -n linkerd -o wide
kubectl describe pod <your-app-pod-name> -n <your-app-namespace> | grep -C 2 "Container ID"

You will see the linkerd-proxy container, but no linkerd-proxy-init container. The network configuration within the pod will be managed by the CNI plugin.

The most surprising aspect of this configuration is that the CNI plugin doesn’t just inject the proxy; it becomes the primary mechanism for preparing the pod’s network environment for the proxy. It handles the complexities of network namespace manipulation that were previously offloaded to the initContainer. This means the CNI plugin needs to be robust and handle all networking setup, including iptables rules for traffic redirection, directly.

The next challenge you might encounter is ensuring that your Kubernetes admission controller configuration correctly permits the linkerd-proxy container to be added to pods, especially if you have strict admission controls in place.

Want structured learning?

Take the full Linkerd course →