Service Mesh Basics with Istio and Linkerd in Kubernetes
Learn how a thin layer of proxies can add encryption, retries, traffic control, and rich metrics to every conversation between your services, without changing a line of application code, and when that trade is worth making.
Imagine a big company where hundreds of employees phone each other all day. Some calls drop, some are slow, and nobody knows who is calling whom. Now imagine the company hires a personal assistant for every employee. Each assistant answers the phone, checks the caller’s identity, redials when a call fails, and writes every call in a logbook. The employees just talk as usual, but the whole company becomes safer, more reliable, and easy to understand.
A service mesh is that team of assistants for your applications. Every service gets a small helper program that handles its network conversations, and a central office tells all the helpers how to behave. This tutorial explains the idea from the ground up, shows how the two best-known meshes, Istio and Linkerd, work inside Kubernetes, and gives you the honest trade-offs, patterns, and warnings that real teams rely on.
1Why Microservices Need Help Talking
A service mesh solves a problem that only appears once you have many small services. To understand it, we first need to see that problem clearly.
From one big program to many small ones
Older applications were often built as a single large program, called a monolith. All the features lived in one place and talked to each other through ordinary function calls inside the same program. Those calls are fast and almost never fail.
Modern teams often split an application into many small programs called microservices. One service handles logins, another handles payments, another handles search. They run separately, often in different containers, and they talk to each other over the network. A container is a sealed package holding a program and everything it needs, and Kubernetes is the system that runs and manages all those containers across many computers.
The network is not reliable
Network calls are very different from function calls. They can be slow, they can fail halfway, and they can be intercepted. When one service calls another, many things can go wrong: the other service may be starting up, overloaded, or simply gone. Someone on the network could also read or change the message if it is not encrypted.
Every team that builds microservices must therefore deal with the same list of chores. Calls should be retried when they fail, but not forever. Slow calls need time limits. Traffic should be encrypted. Each service should prove who it is. And operators need to see which service is calling which, and how healthy each conversation is.
Think of a city where every shop delivers goods to other shops using its own vans, its own maps, and its own locks. Some shops forget to lock the van, some take wrong turns, and nobody keeps a record. The city would be far better off with a shared, professional courier service that every shop uses, which handles routes, locks, and receipts in the same way for everyone.
The old way: put it in a library
Before service meshes, teams solved these chores by adding code libraries to every application. A library might handle retries, another might handle encryption, another might collect metrics. This works, but it has serious costs. Every language needs its own version of every library. Every update requires rebuilding and redeploying each application. And if one team forgets to add the library, or configures it differently, the whole system has a hole in it.
Retries and Timeouts
Try again when a call fails, but give up after a sensible time so slow services do not drag everyone down.
Encryption
Scramble traffic between services so a spy on the network learns nothing useful.
Identity
Let each service prove who it is, so a service can refuse calls from strangers.
Load Balancing
Spread requests across several copies of a service, and steer away from slow or broken ones.
Traffic Control
Send a small share of requests to a new version, or route special requests to a special place.
Observability
Record how many calls happen, how many fail, and how long they take, for every pair of services.
All six chores are about the conversation between services, not about the business logic inside them. That is why they can be moved out of the application and into a shared layer that treats every service the same way.
2Service Mesh: The Core Idea
A service mesh moves the networking chores out of your code and into a layer of small proxies that sit beside every service.
The idea in plain words
A service mesh is a dedicated layer of infrastructure that manages communication between services. It works by placing a small network program, called a proxy, next to each service. A proxy is a middleman that receives a message on behalf of someone else and passes it along. In a mesh, all traffic going into or out of a service passes through its proxy first.
Because every message flows through a proxy, the proxy can do useful work on it: encrypt it, retry it, time it, count it, or refuse it. The application itself knows nothing about this. It sends a plain request as if it were talking directly to the other service, and the proxies take care of everything else.
Picture every office in a building having its own receptionist. Staff hand outgoing letters to the receptionist, who puts them in a secure envelope, notes the time, and sends them off. Incoming letters are opened by the receptionist, who checks the sender’s identity before handing the contents to the staff. The staff just write and read letters, and the receptionists give the whole building a consistent, secure post system.
The two halves of a mesh
Every service mesh has two parts. The proxies that carry traffic are called the data plane, because the data flows through them. The central program that tells all the proxies what to do is called the control plane. The control plane does not carry any user traffic. It only sends instructions, such as which services exist, which rules to apply, and which security certificates to use. We look at both in detail in Chapter 4.
The vocabulary you will use
Proxy
A small program that receives traffic for a service and forwards it, adding features on the way.
Sidecar
A proxy container placed in the same pod as the application, sharing its network, like a sidecar attached to a motorbike.
Data Plane
The collection of all proxies that carry real traffic between services.
Control Plane
The central manager that configures the proxies, hands out identities, and tracks which services exist.
mTLS
Mutual TLS, a way of encrypting traffic where both sides prove their identity to each other, not just one.
East-West Traffic
Traffic between services inside the cluster. It is called east-west because diagrams draw it sideways.
North-South Traffic
Traffic entering or leaving the cluster from the outside world, such as a user’s browser request.
Golden Signals
Four basic health measures for a service: latency, traffic, errors, and saturation.
Istio and Linkerd are the two best-known service meshes for Kubernetes. Both follow the idea above, and both are open source projects that have reached the top maturity level of the Cloud Native Computing Foundation. They differ in size, features, and philosophy, which we compare in Chapter 11.
3Kubernetes Building Blocks You Need First
A mesh is built on top of ordinary Kubernetes objects. Knowing them makes the rest of the tutorial feel natural.
Pod
The smallest unit Kubernetes runs. A pod holds one or more containers that share a network address and are started together.
Service
A stable name and address in front of a changing group of pods, so callers do not need to know individual pod addresses.
kube-proxy
The component on each node that forwards traffic sent to a Service to one of its pods, working at the connection level.
Namespace
A named room inside the cluster used to group resources. Meshes are often switched on one namespace at a time.
Label and Annotation
Small tags on objects. A label on a namespace is the usual way to tell a mesh “please manage the pods here”.
Admission Webhook
A checkpoint that can change objects as they enter the cluster. Meshes use one to add proxies to new pods automatically.
Custom Resource
A new kind of Kubernetes object added by a project. Meshes use them to hold rules such as routes and security policies.
ServiceAccount
The identity a pod uses inside Kubernetes. Meshes build each service’s cryptographic identity from it.
Ingress and Gateway
The front door that lets outside traffic into the cluster and routes it to the right Service.
Init Container
A short-lived container that runs before the main containers, often used to prepare the network settings of a pod.
What Kubernetes already does for traffic
Kubernetes already gives you basic networking. A Service provides a stable address, and kube-proxy spreads connections across the pods behind it. This is enough for many simple applications, but it works at the level of connections, not individual requests. It does not understand HTTP, does not encrypt anything by itself, and cannot retry, time out, or split traffic by percentage.
This gap is exactly what a mesh fills. The mesh does not replace Kubernetes networking. It builds a smarter layer on top of it, using the pods, Services, and labels that already exist.
Kubernetes networking is like a postal system that delivers a sealed parcel to the right building. It does not read the parcel, guarantee that it is tracked, or try again if the parcel is lost. A service mesh is like adding a premium courier on top, which tracks each parcel, checks the recipient’s identity, and resends if needed.
Many people believe that a service mesh replaces Ingress, network policies, or Kubernetes Services. It does not. The mesh works together with them, and often uses them as its starting information about which services exist.
Why per-request matters
Modern services often use long-lived connections, especially with the gRPC protocol, which sends many requests over one connection. Because kube-proxy balances connections rather than requests, all the requests on one long connection may end up at a single pod, while other pods sit idle. A mesh proxy understands each request separately, so it can spread them evenly across all pods.
4The Data Plane and the Control Plane
Every mesh is split into workers that handle traffic and managers that give instructions. Keeping these two roles apart is the secret of its design.
The data plane: the proxies
The data plane is made of the proxies that sit beside your services. They are the busy workers. Every request goes through them, so they must be fast, small, and reliable. Each proxy knows how to find other services, balance load, encrypt and decrypt traffic, apply retries and timeouts, and record metrics.
Istio uses a well-known open source proxy called Envoy. Envoy is powerful and highly configurable, which is why many other tools use it too. Linkerd takes a different route and uses its own tiny proxy, written in the Rust language and built only for Linkerd. It does fewer things, but it is designed to be lean and simple.
The control plane: the managers
The control plane is the brain. It does not touch user traffic. Instead, it watches the cluster, learns which services and pods exist, and pushes that knowledge, along with your rules, to every proxy. It also acts as a certificate authority, meaning it issues the digital identity documents that let services prove who they are.
Because the control plane is separate, a short outage in it does not stop traffic. The proxies already have their instructions and keep working. They just cannot receive new instructions until the control plane returns.
flowchart LR
CP["Control plane: gives config, identities, and service lists"] -->|"instructions"| PA["Proxy beside Service A"]
CP -->|"instructions"| PB["Proxy beside Service B"]
AA["Service A"] --- PA
PA -->|"encrypted traffic"| PB
PB --- BB["Service B"]
Read the figure from the top. The control plane sends instructions to each proxy, shown by the arrows leaving it. The services themselves, drawn as A and B, only talk to their own proxy, shown by the plain lines. The real traffic goes from proxy to proxy along the encrypted arrow. The control plane never appears on that path.
Consider an airport. The control tower does not carry passengers. It sends instructions to pilots about routes, timing, and safety. The aircraft are the ones that actually carry people. If the tower loses radio for a moment, planes already in the air continue on their assigned paths, though no new instructions arrive until contact returns.
Discover
The control plane watches Kubernetes for Services, pods, and their addresses, so it always has a current map of the cluster.
Configure
It combines that map with your rules and sends each proxy the exact instructions it needs.
Identify
It issues each proxy a short-lived certificate so that services can prove who they are to each other.
Update
When pods come and go or rules change, it sends fresh instructions so the proxies stay correct.
Proxies keep their last known configuration when the control plane is unavailable. Existing traffic continues normally. The real risk is a long outage, because certificates expire and new pods cannot get their identity or configuration.
5How the Proxy Gets Into Your Pod
The magic of a mesh is that developers do nothing. This chapter reveals the trick behind that.
Automatic injection
When you want a namespace to be part of the mesh, you mark it with a label or annotation. From then on, whenever a new pod is created in that namespace, Kubernetes sends the pod definition through an admission webhook belonging to the mesh. The webhook quietly adds the proxy container to the pod before the pod is saved. This is called sidecar injection.
Developers write their normal deployment as usual. They never mention the proxy. The mesh adds it automatically, which is why teams can adopt a mesh gradually, one namespace at a time. Pods that already exist do not change until they are restarted, so enabling injection usually needs a rolling restart of the applications.
flowchart TD
A["Namespace is labelled for the mesh"] --> B["A new pod is requested"]
B --> C["API server calls the mesh admission webhook"]
C --> D["Webhook adds the proxy container and network setup"]
D --> E["Pod is scheduled and started with its proxy"]
E --> F["Proxy contacts the control plane for identity and settings"]
Catching the traffic
Adding a proxy is only half of the trick. The application must be forced to send its traffic through that proxy, even though the application does not know the proxy exists. The mesh does this by changing the pod’s network rules so that all outgoing traffic is redirected to the proxy, and all incoming traffic arrives at the proxy first.
This redirection is usually set up by a short init container that runs before the application starts. It needs elevated network permissions to do its job, which some organisations dislike. As an alternative, both Istio and Linkerd offer a network plugin approach that does the redirection at the node level, so pods do not need those permissions.
Imagine that when a new employee joins, the company automatically assigns them a receptionist and changes the office phone system so every call, in and out, goes through that receptionist. The employee does nothing and notices nothing, apart from the fact that calls suddenly become safer and better tracked.
Startup, shutdown, and other lifecycle wrinkles
Because the proxy and the application are separate containers, their starting order matters. If the application starts first and immediately makes a call, the proxy may not be ready yet, and the call fails. Meshes offer settings to hold the application until the proxy is ready. Newer Kubernetes versions also support sidecar containers as a first-class idea, which have a clear start and stop order relative to the main container.
The same issue appears at shutdown. If the proxy stops before the application has finished its last requests, those requests fail. It is also a classic problem with batch jobs, where the job finishes but the proxy keeps running, so the pod never completes. Good mesh setups handle both cases explicitly.
Turning on injection and forgetting that existing pods must be restarted. The namespace looks meshed, but old pods are running without proxies, so the traffic between old and new pods behaves differently than you expect.
You can exclude a specific pod, port, or namespace from the mesh. This is useful for special workloads, such as those using unusual protocols, or for a database that should not have traffic captured.
6Architecture: Istio and Linkerd Side by Side
Both meshes follow the same two-part design, but they package the parts differently. Knowing the pieces helps you read logs and diagrams later.
Istio’s parts
Istio’s control plane is delivered mainly as a single program called istiod. It watches Kubernetes, converts your rules into proxy configuration, sends that configuration to every Envoy proxy, and acts as the certificate authority that issues identities. Older versions split these jobs across several programs, which were merged into one to make Istio simpler to run.
Istio’s data plane is a set of Envoy proxies, one per pod in the classic sidecar mode. Istio also provides gateways, which are Envoy proxies running at the edge of the mesh, handling traffic entering and leaving the cluster. Istio’s rules are written as Kubernetes custom resources, such as routes, destination settings, gateways, and security policies.
Linkerd’s parts
Linkerd’s control plane is made of a few small components. One keeps track of destinations, meaning where each service’s pods are. One acts as the identity authority, issuing certificates. One is the injector, which adds the proxy to new pods. Extension components add features such as a metrics dashboard.
Linkerd’s data plane is its purpose-built Rust proxy, added to every meshed pod. Linkerd deliberately keeps its configuration small. It leans on the newer Kubernetes Gateway API for routing rules and on a small set of its own resources for security policy.
istiod
The single control plane program: discovery, configuration, and certificate authority in one.
Envoy Sidecar
The feature-rich proxy added beside each application container.
Ingress and Egress Gateways
Edge proxies that control traffic entering and leaving the mesh.
Traffic and Security Resources
Kubernetes objects describing routes, destinations, encryption modes, and access rules.
Destination Service
Tells proxies where each service’s pods are and how to reach them.
Identity Service
Issues short-lived certificates to every proxy and acts as the mesh’s certificate authority.
Proxy Injector
The admission webhook that adds the Rust proxy to pods in meshed namespaces.
Micro-Proxy
A lightweight proxy designed to do a small number of things quickly and predictably.
Istio is like a large, fully equipped hotel chain with a service for every request, from concierge to conference rooms, and a big operations office to run it all. Linkerd is like a small, well-run guesthouse that offers the essentials extremely well, with less to learn and less to maintain. Both give you a bed for the night, but they suit different guests.
In both meshes, the control plane holds a root of trust and issues short-lived certificates to proxies. Many production setups plug in their own certificate authority, so the mesh’s trust chain is anchored in something the organisation already controls.
7Internal Working: The Life of a Request
Let us follow a single request from one service to another and see exactly what each proxy does along the way.
Step by step through the mesh
Suppose a checkout service wants to call a payment service. The checkout application sends an ordinary request to the payment service’s name, as it always did. It has no idea a mesh exists. What happens next is a chain of events inside two proxies, shown in the diagram below.
sequenceDiagram
participant C as Checkout App
participant CP as Checkout Proxy
participant SP as Payment Proxy
participant S as Payment App
C->>CP: Plain request to the payment service name
CP->>CP: Pick a healthy pod, apply retry and timeout rules
CP->>SP: Encrypted request using mutual TLS
SP->>SP: Verify caller identity and check access rules
SP->>S: Plain request on the local connection
S-->>SP: Response
SP-->>CP: Encrypted response
CP-->>C: Response, metrics recorded on both sides
The application sends a plain request
The checkout code asks for the payment service by name. The pod’s network rules quietly redirect this outgoing call to the local proxy.
The client proxy decides where to go
It already knows, from the control plane, the list of healthy payment pods. It picks one using a balancing method, and applies any routing, timeout, or retry rules that apply.
The connection is secured
The two proxies set up an encrypted connection, and each shows its certificate to the other. Both sides now know exactly who they are talking to.
The server proxy checks the rules
It reads the caller’s identity from the certificate and checks whether that caller is allowed to reach this service. If not, the request stops here.
The payment application answers
The server proxy hands the plain request to the application, which does its work and sends a response back the same way.
Both proxies record the result
Each proxy counts the request, measures how long it took, and notes whether it succeeded, feeding the dashboards you will meet in Chapter 10.
It is like two offices exchanging a confidential letter through their receptionists. The sender’s receptionist looks up the right recipient, seals the letter, and shows an official badge. The recipient’s receptionist checks the badge, checks the guest list, and only then opens the envelope and hands the letter over. Both keep a record of the exchange.
How proxies know where to send traffic
Proxies do not search for services on their own. The control plane watches Kubernetes for Services and their pods, and continually sends each proxy the current list of addresses for every service. When a pod starts, stops, or becomes unhealthy, the change flows to the proxies within moments. This means the proxy always has a fresh map and never needs to ask a central directory on each request.
Smarter load balancing
Because a mesh proxy understands individual requests, it can balance them far better than the default. Instead of sending each connection to a random pod, it can favour the pod that has been answering fastest. Linkerd, for example, uses a latency-aware method that weighs recent response times. This steers work away from slow or struggling pods before they cause problems.
Every request now passes through two proxies, one on each side. Each hop adds a small delay, usually a few milliseconds or less, and uses some memory and processor time. For most applications this is invisible, but for extremely latency-sensitive systems it must be measured, not assumed.
8Traffic Management
Because every request passes through a proxy, you can shape traffic with rules instead of code. Here are the main tools.
Retries
If a call fails because of a brief problem, the proxy sends it again, possibly to a different pod. This hides short glitches from users. Retries must be limited, and should only be used for requests that are safe to repeat.
Timeouts
A timeout sets how long a proxy waits before giving up on a call. Without timeouts, one slow service can leave callers waiting for ever, and the slowness spreads through the whole system.
Circuit breaking
When a service keeps failing, the proxy temporarily stops sending it traffic, giving it time to recover instead of hammering it further. Istio offers this through outlier detection and connection limits, and recent Linkerd releases also offer forms of circuit breaking.
Traffic splitting
You can send a chosen percentage of requests to one version of a service and the rest to another. This is the basis of canary releases, where a new version first receives a small share of real traffic.
Header-based routing
Rules can look at request headers and send special requests, such as those from testers, to a special version, while everyone else goes to the normal one.
Traffic mirroring
The proxy copies live requests to a second version and discards its answers. You can test a new version with real traffic without any user being affected.
Fault injection
Istio can deliberately add delays or errors to a small share of requests, so teams can test how their systems behave when things go wrong, before real failures happen.
flowchart LR
U["Caller"] --> P["Caller proxy applies the route rule"]
P -->|"90 percent"| V1["Checkout version 1"]
P -->|"10 percent"| V2["Checkout version 2"]
The figure shows a canary release. The caller’s proxy applies a rule that sends ninety percent of requests to version one and ten percent to version two. To roll forward, you change the numbers. To roll back, you set the new version to zero. No pods are added or removed, and no application changes are needed. This is far finer control than Kubernetes offers on its own, where traffic share is tied to the number of pods.
Think of a road with a traffic officer at a junction. The officer can wave nine cars down the old road for every one sent down the new road, hold cars back if the new road jams, or send a decoy car down the new road just to see how it copes. The drivers simply follow the officer’s signals.
If every layer of a system retries three times, one failed call can become dozens of calls hitting an already struggling service, which makes the failure worse. Set retries in one place, cap the total number, and use retry budgets where available.
A request is idempotent if repeating it has the same effect as doing it once, such as reading a record. Retrying a payment request that might already have succeeded could charge a customer twice. Only retry operations that are safe to repeat.
9Security: Identity, mTLS, and Authorization
For many teams, security is the main reason to adopt a mesh. The mesh can encrypt every internal call and decide who may make it.
Identity: proving who you are
In a mesh, every service gets a cryptographic identity. This is a certificate, a digital document that says “this proxy belongs to this service account in this namespace”, signed by the mesh’s certificate authority. The identity is tied to the Kubernetes ServiceAccount of the pod, so it reflects something real rather than a name someone typed in.
Certificates are short-lived, typically valid for about a day, and are rotated automatically by the control plane. If one is stolen, it becomes useless quickly. Nobody needs to copy files, set reminders, or restart applications to renew them.
mTLS: encryption where both sides prove themselves
You may know that ordinary secure web browsing uses TLS, where the website proves its identity to the browser. Mutual TLS, or mTLS, goes one step further: both sides present certificates. The client proxy proves who it is to the server, and the server proxy proves who it is to the client. Everything sent between them is encrypted.
The result is that traffic between meshed services cannot be read or altered by someone on the network, and neither side can pretend to be someone else. All of this happens between the proxies, so the applications need no certificate code at all.
Imagine two diplomats meeting in a corridor. Each shows an official passport, and each checks the other’s passport against a trusted register before speaking. Once both are satisfied, they talk in a sealed room where nobody else can listen. That is mTLS: shown identity on both sides, then private conversation.
Authorization: deciding who may call whom
Knowing who is calling is only useful if you act on it. Authorization policies let you say things like “only the checkout service may call the payment service”, or “only requests using this method on this path are allowed”. The server proxy checks each request against these rules and rejects anything that does not match.
A strong starting position is to deny everything by default and then allow only the specific calls that are needed. This limits the damage if any one service is compromised, because the attacker can only reach what that service was already permitted to call. This idea is often called zero trust.
| Mode | What it does | When to use it |
|---|---|---|
| Permissive mTLS | Accepts both encrypted and plain traffic | During migration, while some pods are not yet meshed |
| Strict mTLS | Accepts only encrypted, authenticated traffic | Once every caller is meshed |
| Allow by default | Any authenticated caller may call anything | Early adoption, with a plan to tighten |
| Deny by default | Only explicitly allowed calls succeed | Target state for sensitive systems |
Hardening the mesh itself
Protect the Control Plane
The control plane issues identities and pushes rules, so restrict who can reach and change it, and keep it patched.
Restrict Who Edits Mesh Rules
Use Kubernetes access control on the mesh’s custom resources, because a bad rule can open or block traffic everywhere.
Use Your Own Root of Trust
Anchor the mesh’s certificates in a certificate authority your organisation already manages and protects.
Control Outbound Traffic
Decide which external addresses services may reach, so a compromised service cannot freely send data out.
Keep Network Policies Too
Kubernetes network policies still block traffic at the network level, protecting anything that bypasses or lacks a proxy.
Move Beyond Permissive Mode
Permissive mode is a stepping stone. Leaving it on for ever means unencrypted traffic is still accepted.
The mesh protects traffic between meshed services. It does not fix vulnerable application code, protect data at rest, or secure pods that were never added to the mesh. Treat it as one strong layer, combined with the others.
10Observability: Metrics, Logs, and Traces
Because every proxy sees every request, a mesh gives you detailed visibility into your system almost for free.
Imagine a road network where every junction has a counter that records how many cars pass, how long they wait, and how many break down. Suddenly you can see the traffic jams, the slow roads, and the dangerous corners without sending anyone out to look. The proxies are those junction counters for your services.
Metrics: the golden signals for every service
Each proxy records, for every service pair, the four golden signals. These are enough to describe the health of almost any service, and the mesh provides them uniformly, whatever language the application uses.
Latency
How long requests take, best viewed as percentiles such as the slowest one percent, not only an average.
Traffic
How many requests arrive per second, per service and per route.
Errors
What fraction of requests fail, including failures caused by timeouts and refused connections.
Saturation
How close resources such as connections and processors are to their limits.
These metrics are exposed in a format that Prometheus, a popular monitoring system, can collect. They can then be drawn as charts in Grafana. Istio has a companion tool called Kiali that draws a live map of which services talk to which, and Linkerd ships a lightweight dashboard that shows success rates and latency for each service and each route.
Logs and access records
Proxies can write an access record for every request, showing who called whom, the method, the path, the response code, and the time taken. These records are very useful when investigating an incident, but they are also very numerous. Most teams turn them on selectively, for specific services or only for failed requests, to control storage cost.
Distributed traces
A trace follows a single user action across many services, showing the time spent in each step. Proxies can create trace records for each hop, but they cannot connect the hops on their own. To link the incoming request to the outgoing calls that it caused, the application must copy certain tracing headers from the request it received into the requests it sends.
Many teams are surprised to learn that a mesh does not give complete traces without any code change. The applications must propagate the tracing headers. Without that, each hop appears as a separate, unconnected trace.
The most valuable and least costly benefit is the uniform metrics. Adopt those first, learn to read them, and add access logs and traces where a real question demands them.
11Istio Versus Linkerd
Both are excellent and mature. The right choice depends on how much you need, and how much complexity you can carry.
| Aspect | Istio | Linkerd |
|---|---|---|
| Proxy | Envoy, general-purpose and very configurable | Purpose-built Rust micro-proxy |
| Philosophy | Broad feature set and many options | Simplicity and a small footprint |
| Learning curve | Steeper, many resources and settings | Gentler, fewer concepts |
| Resource use | Higher, though it varies by setup | Generally lighter, though results vary by workload |
| mTLS out of the box | Automatic, with permissive mode by default | Automatic for meshed pods by default |
| Traffic features | Very rich: gateways, fault injection, mirroring, many match rules | Focused set, leaning on the Gateway API |
| Non-Kubernetes workloads | Broader options for virtual machines and external services | More limited |
| Sidecar-free option | Ambient mode | Sidecar-based design |
| Community status | Graduated project of the Cloud Native Computing Foundation | Graduated project of the Cloud Native Computing Foundation |
How to choose
If your needs are mainly automatic encryption, good metrics, and reliable request handling, Linkerd’s smaller design often gets you there faster and with less to operate. If you need advanced traffic control, edge gateways, extensive policy options, or integration with workloads outside Kubernetes, Istio’s wider toolbox may be worth its extra complexity.
Whichever you pick, judge by your team’s real needs rather than by feature lists. A mesh with a hundred features that nobody understands is worse than a mesh with ten features that everyone can debug at night.
Choosing between them is like choosing between a professional camera with many dials and a good compact camera that mostly works out of the box. The first gives you power and control if you have time to learn it. The second gets excellent everyday photos with very little fuss.
Project governance and release arrangements can change over time. For example, how Linkerd’s stable releases are distributed has changed in recent years. Before committing, check the current release and licensing arrangements of whichever mesh you choose, and confirm they suit your organisation.
12Beyond Sidecars: Ambient and Other Models
The sidecar model is popular but not perfect. Newer designs try to keep the benefits while removing some of the costs.
Why people look beyond sidecars
Adding a proxy to every pod has costs. Each proxy uses memory and processor time, so a cluster with thousands of pods pays that price thousands of times. Pods must be restarted to receive proxy updates. Startup and shutdown ordering causes subtle problems. And the proxy lives inside the application’s own pod, which some security teams dislike.
Istio ambient mode
Istio’s ambient mode removes the per-pod sidecar. Instead, a small shared agent on each node, called ztunnel, handles the basic jobs: encrypting traffic with mTLS and enforcing simple identity-based rules. When an application needs richer features, such as detailed routing by path or header, an optional proxy called a waypoint is added for a namespace or a service. Apps that need only encryption and identity pay a much smaller cost.
Kernel-level approaches
Some networking projects, such as Cilium, use a Linux kernel technology called eBPF to handle much of the work inside the operating system, with shared per-node proxies for the more complex web-level features. These designs also avoid a proxy in every pod, and blur the line between the network layer of the cluster and the mesh.
| Model | Where the proxy runs | Strength | Trade-off |
|---|---|---|---|
| Sidecar | Inside every pod | Strong per-pod isolation and mature tooling | Higher resource cost and lifecycle wrinkles |
| Ambient | Shared per node, plus optional waypoints | Lower cost, no pod restarts to join | Newer model, different debugging habits |
| Kernel-level | In the operating system, with shared proxies for advanced features | Very efficient for basic needs | Depends on kernel features and network layer choices |
| Library in the app | Inside application code | No extra network hop | Per-language work and inconsistent adoption |
The sidecar model gives every office its own receptionist. The ambient model gives each floor one shared reception desk for the basics, and adds a specialist assistant only for offices that need extra help. It costs less, but the specialist arrangements need a little more planning.
Whichever model you use, the ideas in this tutorial hold: a data plane carries traffic, a control plane manages it, identities are issued to workloads, and rules describe how traffic behaves. Learning the sidecar model first is a good foundation for any of the others.
13The Adoption Lifecycle and Running the Mesh
A mesh is not installed once and forgotten. It has a lifecycle, from careful first steps to regular upgrades, and each stage has its own traps.
A safe path to adoption
The safest way to adopt a mesh is slowly. Start with a small, low-risk namespace, watch what happens, and expand only when you understand the results. Turning on everything for every team on the first day is the most common way to turn a promising tool into a painful outage.
flowchart TD
A["Install the control plane"] --> B["Mesh one low-risk namespace"]
B --> C["Observe metrics with permissive mTLS"]
C --> D["Enable strict mTLS"]
D --> E["Add authorization policies"]
E --> F["Add traffic rules: timeouts, retries, canaries"]
F --> G["Expand to more namespaces"]
G --> H["Upgrade and review regularly"]
Prepare the cluster
Check the cluster’s version and capacity, decide how certificates will be managed, and choose the mesh, keeping your team’s skills in mind.
Install and verify
Install the control plane, run the project’s own health checks, and confirm that everything reports healthy before adding any application.
Mesh a pilot namespace
Label one non-critical namespace, restart its pods so they receive proxies, and confirm that they still work exactly as before.
Measure the cost
Record the extra memory, processor use, and latency the proxies add. Use real numbers to plan the rest of the rollout.
Tighten security
Move from permissive to strict mTLS once every caller is meshed, and then introduce authorization rules, starting with the most sensitive services.
Use traffic features deliberately
Add timeouts first, then careful retries, then canary routing for services that release often. Add each feature only when a real need exists.
Expand and standardise
Roll out to more namespaces using a repeatable template, and write down the conventions so teams do not invent their own.
Upgrade on a schedule
Plan regular upgrades of the control plane and the proxies. Falling far behind is risky, because security fixes and compatibility depend on staying current.
Adopting a mesh is like installing a new phone system in a company. You would not swap every desk phone overnight. You would first try it in one team, learn the quirks, and then move department by department, keeping the old system available until each move has proved itself.
Monitoring the mesh itself
Control Plane Health
Watch that the control plane pods are running, restarting rarely, and answering requests quickly.
Configuration Sync
Confirm that every proxy has received the latest configuration. A proxy that is out of date behaves by old rules.
Certificate Expiry
Watch the expiry dates of the root and intermediate certificates. An expired root can stop all encrypted traffic at once.
Proxy Resource Use
Track the memory and processor use of proxies, because they scale with the number of services and the traffic they see.
Version Skew
Check which pods run older proxies than the control plane. Large differences are a warning to restart and upgrade.
Unmeshed Pods
Look for pods in meshed namespaces that lack a proxy, since they quietly bypass encryption and policy.
Teams sometimes install a mesh with a default root certificate that has a fixed lifetime and then forget it. When it expires, proxies can no longer trust each other and traffic stops. Put the expiry date on a calendar and automate rotation before it matters.
14Advantages, Disadvantages, and Trade-offs
A mesh gives a lot, and it costs a lot. An honest comparison helps you decide whether it is right for you, and when.
Advantages
- Encrypts and authenticates service-to-service traffic without changing application code.
- Provides uniform metrics for every service, whatever language it is written in.
- Adds retries, timeouts, and smarter load balancing in one shared place.
- Allows fine-grained traffic control, such as canaries and mirroring.
- Gives central, consistent policy instead of scattered library settings.
- Helps teams see and understand how their services really depend on each other.
Disadvantages / Trade-offs
- Adds a whole new system to install, secure, monitor, and upgrade.
- Uses extra memory and processor time, multiplied by the number of pods.
- Adds a small delay to every request through the extra hops.
- Makes debugging harder, because traffic problems may now be in the proxy layer.
- Has a learning curve that touches networking, security, and Kubernetes at once.
- Creates a shared dependency: a bad mesh configuration can affect many services at once.
Thinking about cost honestly
The cost of a mesh has three parts. The first is machine cost: the proxies use resources, and in a cluster with thousands of pods that becomes real money. The second is people cost: someone must learn, run, and upgrade the mesh, which takes real time. The third is complexity cost: every incident now has one more place where the cause might hide.
These costs are worth paying when the benefits are large. A company with hundreds of services and strict security requirements gains far more than it spends. A team with five services and a simple architecture may gain almost nothing. The key is to ask which specific problems you have today, and whether a mesh is the smallest tool that solves them.
When a mesh may not be the answer
| Your need | Simpler alternative | When a mesh becomes worth it |
|---|---|---|
| Basic encryption between a few services | Encryption inside the application, or network-level encryption | When the number of services grows and consistency becomes hard |
| Canary releases for one service | A progressive delivery tool or ingress weights | When many services need the same fine-grained control |
| Metrics for requests | Application metrics libraries | When you have many languages and uneven coverage |
| Blocking unwanted network paths | Kubernetes network policies | When you need identity-aware, request-level rules |
| Retries and timeouts | A well-chosen client library | When you cannot change every application |
The best mesh adoption stories start with a specific pain, such as a compliance requirement for encryption everywhere, or a need for consistent metrics across many teams. The worst start with the sentence “everyone else has one”.
15Design Patterns and Anti-patterns
Some habits make a mesh calm and useful. Others turn it into an expensive source of surprise.
Patterns that work well
Namespace-by-Namespace Rollout
Bring services into the mesh in small groups, learning and adjusting after each step.
Default Deny with Explicit Allow
Start from denying all calls and open only those paths a service truly needs.
Timeouts Everywhere, Retries Sparingly
Give every call a time limit, and add retries only to safe requests and only in one layer.
Canary Routing for Releases
Send a small share of traffic to a new version and increase it as the metrics stay healthy.
Gateway at the Edge
Handle outside traffic with a gateway that shares the mesh’s policies, so inside and outside rules line up.
Mesh as a Platform Product
Treat the mesh as a product run by a platform team, with clear defaults, templates, and documentation for app teams.
Anti-patterns to avoid
Problem
The mesh is switched on for every namespace at once, on the first day, with every feature enabled.
Why It’s Harmful
Problems in proxies, startup order, unusual protocols, or resource limits appear everywhere at the same time, and nobody knows which change caused which failure.
Correct Approach
Adopt step by step, starting with one low-risk namespace, and add security and traffic features in stages.
Problem
Retries are configured in the application, in the client library, and in the mesh, each with its own count.
Why It’s Harmful
The retries multiply. A single failure can turn into dozens of requests, overloading a service that was already struggling and turning a small problem into an outage.
Correct Approach
Decide where retries live, usually in one layer, and cap the total number of attempts across the whole call chain.
Problem
The mesh stays in permissive mode for ever, and the team assumes traffic is encrypted.
Why It’s Harmful
Permissive mode accepts plain traffic as well. Some calls may be unencrypted and unauthenticated without anyone noticing, giving a false sense of security.
Correct Approach
Use permissive mode only during migration, verify which traffic is still plain, and then switch to strict mode.
Problem
Proxies run without resource requests and limits, and nobody plans for their memory and processor use.
Why It’s Harmful
Proxies can be starved under load, which slows the application, or can consume more than expected and crowd out other pods. Cluster capacity planning becomes wrong across the board.
Correct Approach
Measure real proxy usage, set sensible requests and limits, and include the proxy cost in capacity planning.
16Best Practices and Common Mistakes
A short field guide collected from teams that run meshes in production, and from the mistakes newcomers make.
Best practices
- Start with a clear problem — name the specific need, such as encryption or metrics, before installing anything.
- Pilot in a small namespace — learn how the mesh behaves with real workloads before widening the rollout.
- Measure the overhead — record proxy resource use and latency so decisions rest on numbers.
- Use your own certificate authority — anchor trust in a system your organisation controls and rotates.
- Move to strict mTLS — treat permissive mode as temporary and confirm that no plain traffic remains.
- Set timeouts on every route — no call should be allowed to wait for ever.
- Limit and coordinate retries — one layer, a small cap, and only for safe requests.
- Handle startup and shutdown order — make sure applications wait for their proxy and finish their work before it stops.
- Propagate tracing headers — update applications so traces link across services.
- Keep network policies — they still protect anything that bypasses the mesh.
- Upgrade regularly — plan control plane and proxy upgrades, and test them on non-critical namespaces first.
- Document conventions — publish defaults and templates so teams do not each invent their own rules.
Common mistakes
Labelling a namespace does not change running pods. Until they are restarted, they run without proxies, and your policies and encryption do not apply to them.
Some traffic, such as certain database protocols or health checks from outside the mesh, can behave badly when captured. Exclude those ports or workloads on purpose, and test them explicitly.
When a call fails, the cause might be the application, the network, or the proxy. Teams that always blame the mesh waste time, and teams that never suspect it miss real configuration errors. Learn to read the proxy’s own logs and metrics side by side with the application’s.
Run a game day where you deliberately break the control plane, expire a certificate in a test cluster, or misconfigure a route. Learning how the mesh fails on a calm afternoon is far cheaper than learning at midnight in production.
17Real-World and Industry Examples
Service meshes appear wherever many services must talk safely, and each industry leans on a different part of the toolbox.
Where the ideas came from
The Envoy proxy, which powers Istio, originated at the ride-sharing company Lyft to solve exactly the problems this tutorial describes. Istio was started by a group of large technology companies, and Linkerd was one of the earliest projects to use the phrase service mesh. Both grew from practical needs in companies running many services.
Regulated industries and encryption everywhere
Banks and healthcare organisations often must show that internal traffic is encrypted and that only approved services can reach sensitive data. A mesh with strict mTLS and authorization policies gives them a consistent, auditable way to prove it, without asking every application team to implement security themselves.
E-commerce during busy seasons
Online retailers use retries, timeouts, and circuit breaking so that one slow service, such as recommendations, does not drag down checkout. Canary routing lets them release changes carefully before a major sales event.
Gradual migration from a monolith
Companies breaking a large application into services use traffic rules to move one feature at a time. A route sends a small share of requests for that feature to the new service, and the share grows as confidence grows, with an instant way back if problems appear.
Platforms serving many internal teams
Platform teams offer the mesh as a shared service. Application teams get encryption, metrics, and safe defaults automatically, and the platform team keeps the mesh upgraded and secure, creating a common baseline across the whole organisation.
Systems built around gRPC
Applications that use gRPC often struggle with uneven load, since one long connection can carry many requests to a single pod. A mesh balances each request separately, which can smooth out load and improve response times without changing the applications.
Multi-cluster and multi-region setups
Both meshes can connect services across several clusters, so a service in one cluster can call a service in another using the same identity and policy model. This helps with resilience, regional deployments, and migrations between clusters.
18Frequently Asked Questions
Quick answers to the questions that come up most often when teams first meet a service mesh.
Not for the basic features such as encryption, metrics, and load balancing. The proxies handle them. For full distributed tracing, applications do need to pass along tracing headers.
No. A gateway mainly handles traffic entering the system from outside, while a mesh mainly handles traffic between services inside. Meshes often include their own gateways so that both work together.
No. It builds on them. Services and pods provide the basic map of the cluster, and the mesh adds smarter behaviour on top.
Each proxy uses some memory and processor time, and each request passes through two extra hops that add a small delay. The exact numbers depend on the mesh, the traffic, and the features used, so measure in your own environment.
Existing traffic keeps flowing, because proxies keep their last configuration. New pods cannot get identities or settings, and certificates cannot be renewed, so a long outage becomes a real problem.
Both meshes enable mTLS between meshed pods automatically. Istio often starts in a permissive mode that also accepts plain traffic, so you should move to strict mode when your services are ready.
Running two meshes over the same pods is generally a bad idea, because both try to capture the same traffic. Choose one for each set of workloads.
Usually not. With a few services, simpler tools such as network policies, a good client library, and application metrics are often enough. A mesh pays off as the number of services and teams grows.
The proxy container keeps running after the job’s own container exits, so the pod never completes. Meshes and newer Kubernetes sidecar support offer ways to stop the proxy when the main work is done.
Yes, to a degree. Proxies can encrypt and balance plain TCP traffic, and can report basic metrics. Rich features such as path routing and per-request retries apply mainly to protocols the proxy understands, such as HTTP and gRPC.
Remove injection labels from namespaces, restart pods so they lose their proxies, and then uninstall the control plane. Doing it namespace by namespace, in the reverse order of adoption, keeps the risk low.
Not soon. Sidecars remain widely used and well supported. Alternatives such as ambient mode are growing, and many teams will use a mix depending on their needs.
19Summary and Key Takeaways
A service mesh moves the repeated work of service-to-service communication out of your applications and into a shared layer of proxies, managed by a central control plane. In return for a modest cost in resources and complexity, you gain encryption, identity, smarter load balancing, fine traffic control, and detailed metrics, all without rewriting your code. Istio offers a very wide toolbox, and Linkerd offers a smaller, simpler design, and newer models such as ambient mode try to reduce the cost of the sidecar approach.
The mesh is a powerful tool, not a magic one. It needs careful adoption, regular upgrades, and honest measurement. It works best when it solves a problem you actually have, and when it is run as a well-documented platform rather than a surprise. Teams that follow those habits find that the mesh quietly makes their systems safer and easier to understand.
Key Takeaways
- A mesh is proxies plus a manager — a data plane of proxies carries the traffic, and a control plane configures them and issues identities.
- Injection is automatic, restarts are not — proxies are added to new pods by a webhook, and existing pods must be restarted to receive them.
- Security is the strongest reason to adopt — automatic mTLS and authorization policies protect internal traffic, but only if you move beyond permissive mode.
- Traffic rules replace code — timeouts, careful retries, canary splits, and mirroring are all configuration, with retry storms as the main danger.
- Metrics come almost free — golden signals for every service pair are available immediately, while full traces still need applications to pass headers along.
- Choose by need, not by hype — Istio suits broad, advanced needs, Linkerd suits simplicity and low overhead, and small systems may not need a mesh at all.
- Adopt gradually and run it well — pilot in one namespace, measure overhead, watch certificates and versions, and treat the mesh as a platform product.