Linkerd can make external traffic visible to your services, but it requires careful configuration of your ingress controller.

Let’s see Linkerd in action with Nginx Ingress.

Imagine this scenario: you have a web service running in Kubernetes, exposed via Nginx Ingress. You want Linkerd to manage traffic to this service, injecting its sidecar, and providing observability.

apiVersion: v1
kind: Service
metadata:
  name: my-app-service
  namespace: default
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: my-app
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: my-app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-app-service
            port:
              number: 80

Here, Nginx Ingress routes my-app.example.com/ to my-app-service on port 80.

Now, when you install Linkerd, you’ll typically inject the linkerd-proxy sidecar into your application pods. However, traffic coming from Nginx Ingress to your application pod won’t automatically go through the linkerd-proxy if the service is configured to point directly at the pod’s application port.

The key is to have Nginx Ingress target the linkerd-proxy’s HTTP port (usually 4140) instead of the application’s direct port. This is often achieved by creating a new Kubernetes Service that acts as the intermediary, pointing to the linkerd-proxy’s port.

apiVersion: v1
kind: Service
metadata:
  name: my-app-service-k8s
  namespace: default
spec:
  ports:
  - port: 80
    targetPort: 4140 # Linkerd proxy's HTTP port
    protocol: TCP
    name: http
  selector:
    app: my-app

Then, you update your Nginx Ingress resource to point to this new my-app-service-k8s service:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: my-app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-app-service-k8s # Point to the new service
            port:
              number: 80

With this setup, Nginx Ingress sends traffic to port 80 of my-app-service-k8s. This service, in turn, directs that traffic to port 4140 on your application pods (where the linkerd-proxy is listening). The linkerd-proxy then processes the traffic, applies policies, collects metrics, and finally forwards it to the actual application on port 8080.

This allows Linkerd to fully observe and control traffic originating from outside the mesh, even when using a traditional ingress controller like Nginx.

The one thing most people don’t realize is that the linkerd-proxy injects itself into the pod and listens on a specific port (4140 for HTTP by default) for incoming traffic that it will then forward to the application’s original port (e.g., 8080). The ingress controller needs to be configured to send traffic to that proxy port, not directly to the application’s port within the pod. This means you often need an extra Kubernetes Service resource that selects the application pods but exposes the proxy’s port, and then have your ingress point to this new service.

The next step is to explore how to configure mTLS for this external traffic.

Want structured learning?

Take the full Linkerd course →