CtrlK
BlogDocsLog inGet started
Tessl Logo

nitinjain999/platform-skills

Production-grade platform engineering handbook — Kubernetes, Terraform, Flux CD, GitHub Actions, AWS, and more.

67

Quality

84%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

linkerd.mdreferences/

Linkerd Reference

Contents

  • Scope
  • Architecture
  • Installation
  • Mesh injection
  • mTLS
  • Traffic management
  • Observability
  • Multi-cluster
  • Troubleshooting

Scope

Use Linkerd for:

  • Automatic mutual TLS between workloads without application code changes
  • L7 observability: golden signals (success rate, latency, RPS) per route and deployment
  • Traffic management: retries, timeouts, canary splits via HTTPRoute
  • Multi-cluster service mirroring with encrypted cross-cluster traffic

Do not expect Linkerd to replace:

  • Ingress controllers (Linkerd handles east-west mesh traffic, not north-south ingress)
  • Network policies (Linkerd authorization policy complements, not replaces, Kubernetes NetworkPolicy)
  • DNS or service discovery (Linkerd wraps existing Kubernetes DNS)

Architecture

Control plane

ComponentRole
destinationResolves service endpoints, traffic policies, and route configurations
identityIssues short-lived workload certificates (default 24h) for mTLS
proxy-injectorWebhook that injects the sidecar proxy into pods at admission time

Data plane

Each meshed pod gets a linkerd-proxy sidecar (written in Rust). The proxy intercepts all inbound and outbound traffic transparently using iptables rules added by the linkerd-init init container.

The proxy handles:

  • mTLS negotiation and certificate validation
  • Retries, timeouts, and circuit breaking
  • Metrics export on :4191/metrics (scraped by Prometheus)

Installation

Prerequisites

  • Kubernetes 1.22+
  • linkerd CLI matching the control plane version
  • A trust anchor certificate (self-signed or cert-manager)

Control plane install

# Pre-flight checks
linkerd check --pre

# Install CRDs
linkerd install --crds | kubectl apply -f -

# Install control plane
linkerd install | kubectl apply -f -

# Verify all components are healthy
linkerd check

Using cert-manager for the identity CA

Back the Linkerd identity issuer with cert-manager so the intermediate CA rotates automatically:

# Certificate issued by cert-manager for Linkerd identity
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: linkerd-identity-issuer
  namespace: linkerd
spec:
  secretName: linkerd-identity-issuer
  duration: 48h
  renewBefore: 25h
  issuerRef:
    name: linkerd-trust-anchor    # ClusterIssuer backed by your root CA
    kind: ClusterIssuer
  commonName: identity.linkerd.cluster.local
  dnsNames:
    - identity.linkerd.cluster.local
  isCA: true
  privateKey:
    algorithm: ECDSA
  usages:
    - cert sign
    - crl sign
    - server auth
    - client auth

Then install Linkerd pointing at cert-manager's output:

linkerd install \
  --identity-external-issuer \
  | kubectl apply -f -

Linkerd Viz (observability extension)

linkerd viz install | kubectl apply -f -
linkerd viz check

Mesh injection

Namespace-level injection

Annotate a namespace to auto-inject the proxy into every new pod:

apiVersion: v1
kind: Namespace
metadata:
  name: payments
  annotations:
    linkerd.io/inject: enabled

Existing pods are not re-injected automatically. Rollout the deployment after annotating:

kubectl rollout restart deployment -n payments

Pod-level override

Opt a specific pod out of injection inside an injected namespace:

spec:
  template:
    metadata:
      annotations:
        linkerd.io/inject: disabled

Verify injection

# Check which pods in a namespace are meshed
linkerd check --namespace payments

# Show proxy version and injection status per pod
kubectl get pods -n payments -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.annotations.linkerd\.io/inject}{"\n"}{end}'

What not to inject

  • kube-system and kube-public — control plane components will break
  • Linkerd's own linkerd namespace — already meshed differently
  • Jobs and CronJobs that use hostNetwork: true
  • Daemonsets that must run before the proxy is ready (e.g., CNI plugins)

mTLS

Linkerd automatically establishes mTLS between all meshed pods. No certificate management or application changes are needed.

How it works

  1. proxy-injector injects the sidecar at pod admission
  2. On startup, the proxy fetches a short-lived SPIFFE-compatible certificate from the identity component
  3. The certificate encodes the pod's Kubernetes ServiceAccount as the identity
  4. All proxies validate peer certificates before forwarding traffic

Verify mTLS is active

# Show secured/unsecured edges between deployments
linkerd viz edges deployment -n payments

# Output includes: SRC, DST, SECURED (yes/no)
# If a pod is unmeshed, the edge shows "no" — traffic is plaintext

Authorization policy

Restrict which identities can call a service (replaces or supplements NetworkPolicy):

apiVersion: policy.linkerd.io/v1beta3
kind: Server
metadata:
  name: payments-api
  namespace: payments
spec:
  podSelector:
    matchLabels:
      app: payments-api
  port: 8080
  proxyProtocol: HTTP/2
---
apiVersion: policy.linkerd.io/v1beta3
kind: AuthorizationPolicy
metadata:
  name: payments-api-allow-checkout
  namespace: payments
spec:
  targetRef:
    group: policy.linkerd.io
    kind: Server
    name: payments-api
  requiredAuthenticationRefs:
    - name: checkout-sa
      kind: MeshTLSAuthentication
      group: policy.linkerd.io
---
apiVersion: policy.linkerd.io/v1beta3
kind: MeshTLSAuthentication
metadata:
  name: checkout-sa
  namespace: payments
spec:
  identities:
    - "checkout.payments.serviceaccount.identity.linkerd.cluster.local"

Traffic management

Linkerd uses the Kubernetes Gateway API (HTTPRoute) for traffic splitting and routing rules.

Canary traffic split

Route 90% of traffic to stable, 10% to canary:

apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: payments-api-canary
  namespace: payments
spec:
  parentRefs:
    - name: payments-api
      kind: Service
      group: core
      port: 8080
  rules:
    - backendRefs:
        - name: payments-api-stable
          port: 8080
          weight: 90
        - name: payments-api-canary
          port: 8080
          weight: 10

Retries

apiVersion: policy.linkerd.io/v1alpha1
kind: HTTPLocalRateLimitPolicy
metadata:
  name: payments-retry
  namespace: payments

For retries, use annotations on the HTTPRoute or configure via the Linkerd retry annotation on the Service:

# Retry on 5xx responses, up to 2 retries
metadata:
  annotations:
    retry.linkerd.io/http: "5xx"
    retry.linkerd.io/limit: "2"

Timeouts

# Per-route timeout via HTTPRoute
spec:
  rules:
    - timeouts:
        request: 10s
        backendRequest: 5s

Observability

Golden signals per deployment

# Success rate, RPS, and latency for all deployments in a namespace
linkerd viz stat deploy -n payments

# Drill into a specific deployment
linkerd viz stat deploy/payments-api -n payments

# Per-route breakdown
linkerd viz stat httproute -n payments

Live request tracing

# Tap live traffic to a deployment (shows headers, status codes, latency)
linkerd viz tap deploy/payments-api -n payments

# Filter to specific path
linkerd viz tap deploy/payments-api -n payments --path /api/v1/charge

Prometheus integration

Linkerd proxies expose metrics on port 4191. Scrape them with a PodMonitor (Prometheus Operator):

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: linkerd-proxy
  namespace: monitoring
spec:
  namespaceSelector:
    any: true
  podMetricsEndpoints:
    - port: linkerd-admin
      path: /metrics
  selector:
    matchLabels:
      linkerd.io/control-plane-ns: linkerd

Key metrics to alert on:

MetricAlert threshold
request_total{direction="inbound", classification="failure"}> 1% of total
response_latency_ms_bucket{le="1000"}p99 > 1000ms
tcp_open_connectionssustained spike vs. baseline

Multi-cluster

Linkerd multi-cluster mirrors services from one cluster into another using a gateway and a ServiceMirror controller.

Requirements

  • Both clusters must share the same trust anchor (root CA)
  • Clusters must have network connectivity to each other's gateway LoadBalancer IPs

Link clusters

On the target cluster (where the service lives):

# Install multi-cluster extension
linkerd multicluster install | kubectl apply -f -
linkerd multicluster check

# Generate link credentials for the source cluster
linkerd multicluster link --cluster-name production > link-production.yaml

On the source cluster (where you want to consume the service):

kubectl apply -f link-production.yaml
linkerd multicluster check

Mirror a service

Label the service in the target cluster to make it available cross-cluster:

metadata:
  labels:
    mirror.linkerd.io/exported: "true"

The ServiceMirror controller creates a mirrored service in the source cluster named <service>-<cluster-name> (e.g., payments-api-production). Traffic to that service is tunnelled over mTLS to the target cluster's gateway.

Verify mirroring

# On source cluster — should see mirrored services
kubectl get svc -n payments | grep "production"

# Check mirroring is healthy
linkerd multicluster gateways

Troubleshooting

Structure

For every Linkerd issue: identify the layer (control plane / data plane / policy / multi-cluster), collect evidence, form a hypothesis, then fix.


Symptom: Pods not getting proxies injected

Evidence to collect:

kubectl describe namespace <ns> | grep -i inject
kubectl get mutatingwebhookconfigurations linkerd-proxy-injector -o yaml | grep -A5 namespaceSelector
linkerd check

Likely causes:

  • Namespace missing linkerd.io/inject: enabled annotation
  • Pod has linkerd.io/inject: disabled override
  • proxy-injector webhook is failing — check linkerd check output

Symptom: mTLS edges showing no (plaintext)

Evidence to collect:

linkerd viz edges deployment -n <namespace>
kubectl get pods -n <namespace> -o wide  # check both src and dst are meshed
linkerd check --namespace <namespace>

Likely causes:

  • One side of the connection is not meshed (check both pods)
  • Pod was running before namespace was annotated and hasn't been rolled

Fix:

kubectl rollout restart deployment/<name> -n <namespace>

Symptom: linkerd check reports certificate expiry or identity errors

Evidence to collect:

linkerd check
kubectl get secret linkerd-identity-issuer -n linkerd -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -dates

Likely causes:

  • Issuer certificate expired (cert-manager rotation failed)
  • Trust anchor mismatch after manual rotation

Fix: rotate using cert-manager renewal or linkerd upgrade --identity-external-issuer.


Symptom: High latency after enabling Linkerd

Evidence to collect:

linkerd viz stat deploy -n <namespace>
linkerd viz tap deploy/<name> --max-rps 10
kubectl top pods -n <namespace>  # check proxy CPU

Likely causes:

  • Proxy CPU limit too low (default is 1 CPU) — under heavy load the proxy queues requests
  • Retry storms amplifying load

Fix: increase proxy resource limits via annotation:

annotations:
  config.linkerd.io/proxy-cpu-limit: "2"
  config.linkerd.io/proxy-memory-limit: "256Mi"

Symptom: Multi-cluster mirrored service unreachable

Evidence to collect:

linkerd multicluster gateways          # check gateway status
kubectl get svc -n linkerd-multicluster  # check gateway LB IP is assigned
linkerd check --multicluster

Likely causes:

  • Gateway LoadBalancer IP not yet assigned
  • Firewall blocking port 4143 between cluster networks
  • Trust anchor mismatch between clusters

BEFORE_AFTER.md

CHANGELOG.md

CODE_OF_CONDUCT.md

COMMANDS.md

CONTRIBUTING.md

EDITOR_INTEGRATIONS.md

GETTING_STARTED.md

HOW_IT_WORKS.md

install.sh

INSTALLATION.md

LAUNCH.md

PROMPTS.md

QUICKSTART.md

README.md

renovate.json

SECURITY.md

SKILL.md

tessl.json

tile.json