Skip to main content

Sidecar & Service Mesh

Editorial

Introduction

API Gateway & BFF ended with an honest admission. Everything the front door does, authenticate, encrypt, retry sensibly, observe, is only enforced at the front door. The moment a request is inside your fleet, service calling service, east to west, there is no door for those rules to live behind. Yet every one of those hops still needs them.

So look at what your services are actually carrying. Every service that calls another needs timeouts, retries with backoff, a circuit breaker, TLS, a discovery client, and telemetry, the whole caller's discipline from Calling Services Safely: Timeouts, Retries & Backoff onward. Now count your languages. A Go service, a Java service, a Python service, a Node service: that discipline is written four times, in four languages, by four teams, and it must stay correct and identical in all four.

This lesson is about the pattern that refuses to accept that grid. The idea is almost rude in its simplicity: if the same concerns appear in every service, they do not belong in the services. Take the networking code out of the application and put it in a separate process standing beside the application. Do that everywhere, wire the standers-beside together under one brain, and you have built the thing this lesson names: a service mesh.

Infographic titled: written four times, or moved out once, with a legend reading timeout, TLS, retry and trace. The left panel, in every service, shows four app boxes labelled Go, Java, Python and Node, each stuffed with the same four red duty chips repeated over and over, captioned: written four times, synced by hope. The right panel, beside every service, shows the same four apps slimmed to app only, each connected to a violet proxy box carrying the four duties once, captioned: one build, upgraded once. The dark band states the rule: if the same concerns appear in every service, they do not belong in the services. A closing note adds that proxies plus one brain make a service mesh.

The Library Era, and Its Two Taxes

The first serious answer to the grid was the obvious one: put the discipline in a library. Netflix built Hystrix for circuit breaking and its siblings for discovery and load balancing. Twitter built Finagle. Google built Stubby. You linked the library into your service, made every remote call through it, and inherited a decade of hard-won networking judgment for free.

It worked, and it collected two taxes that grew with the company.

The polyglot tax. Hystrix is Java. Finagle is JVM. Stubby was C++ and friends. The moment your fleet grows a second language, and Microservices — and When to Split told you team autonomy makes that nearly inevitable, someone re-implements the whole discipline in that language. Every behaviour, every bug fix, every default, ported and kept in sync, forever. The grid is back: concerns times languages.

The upgrade tax, and this one is sneakier. A library links into the application. Fix a bug in the retry logic and the fix ships only when every one of your services rebuilds and redeploys with the new version. In a fleet of a hundred services owned by thirty teams, that is not an upgrade. That is a campaign. Quarters pass with half the fleet on the old behaviour and half on the new, and the difference is invisible until it isn't. The libraries were also invasive: they only work if every developer remembers to make every call through them. One bare HTTP call and the discipline silently has a hole in it.

Notice the shape of both taxes. They are the same two bills the general-purpose API charged in API Gateway & BFF: an engineering cost and a coordination cost. And the cure will rhyme too: move the shared thing out of everyone's code and give it a single owner.

The Sidecar: Out of Process, Beside the Process

The escape from both taxes is a change of address. Take the networking discipline out of the application's process entirely and run it as its own small process, deployed beside every service instance, one for one. The name is literal: a motorcycle sidecar, bolted to the vehicle, going everywhere it goes. The application talks to the outside world only through its sidecar: outbound calls leave through it, inbound calls arrive through it. In practice the sidecar is a small, fast proxy.

Watch both taxes die. The proxy is a separate program, so it does not care what language the application is written in. Go, Java, Python, Node: one proxy implementation serves them all, and the polyglot tax drops to zero. And because the proxy is a separate process, upgrading the networking discipline means replacing the proxy, not rebuilding the application. The upgrade campaign collapses into a rollout the platform team can run without asking thirty other teams for a deploy.

Two mechanics make this invisible to the application. The app and its sidecar are deployed, scheduled and scaled as one unit, a pod in Kubernetes vocabulary, whose full story belongs to Kubernetes: The 20% That Matters. And the application does not even have to choose the proxy: the platform quietly redirects the pod's network traffic through it, so from the app's point of view the network simply became better behaved overnight.

What moves into the sidecar is precisely the caller's discipline you already own as ideas: timeouts and retries from Calling Services Safely: Timeouts, Retries & Backoff, breakers from Circuit Breakers, encryption from TLS: Security's Latency Bill, the client-side lookup from Service Discovery, plus metrics for every call it carries. The application is left holding only its business logic and a plain, naive HTTP client pointed at localhost. The smartness moved out.

This is not a theoretical pattern. Lyft built a proxy called Envoy for exactly this job: development started in May 2015, first deployment that September, and by the summer of 2016 every piece of traffic at Lyft, edge and service-to-service alike, flowed through Envoy: a hundred-plus services, millions of requests a second. Their stated goal is the best one-line summary of the pattern ever written: make the network transparent to applications.

Diagram titled: one pod, opened up. Inside a dashed pod boundary sit two boxes: your app, holding only business logic and a naive client, and the sidecar proxy beside it carrying the timeout, TLS, retry and trace duties for any language. The app's only arrow is a short localhost hop into the proxy, while the green outbound and amber inbound traffic arrows enter and leave the pod exclusively through the proxy. A green band notes that upgrading the discipline means replacing the proxy, not the app, and the dark band closes with Lyft's goal for Envoy: make the network transparent to applications.

Two Planes: The Mesh Is Born

One sidecar is a convenience. A fleet of them creates a problem you have met twice already in this course: a hundred proxies, each holding routing tables, retry policies, certificates, and discovery data, all of which change constantly. Who tells them?

The answer splits the system into two planes, and the vocabulary matters because every mesh, every cloud, and every interview uses it.

The data plane is the proxies themselves: the layer every request actually flows through. It does the work, per call, per packet: route, balance, encrypt, retry, observe. If the data plane stops, traffic stops.

The control plane is one service that touches no request at all. Its only job is to hold the desired state, which services exist, what the retry policy is, who may talk to whom, which certificates are current, and push that configuration out to every proxy, continuously. If the control plane stops, traffic keeps flowing on the last configuration every proxy holds; what stops is change.

You have seen this exact division before. The registry in Service Discovery was control plane; the requests it steered were data plane. The rule you learned there, keep the planes apart so a control-plane outage never stops flowing traffic, is load-bearing here too, and the meshes honour it: proxies keep serving from their last pushed config when the brain goes quiet.

A service mesh, then, is nothing more mysterious than this: a data plane of sidecars on every service, plus a control plane that programs them. Istio is a control plane programming Envoy sidecars. Linkerd is the same shape with its own purpose-built micro-proxy. The names differ; the anatomy is identical.

Diagram titled: mesh anatomy, two planes. Three pods for checkout, catalog and orders are stacked in a column, each holding an app and its sidecar proxy. Solid violet arrows flow vertically from proxy to proxy, labelled as the data plane where every request travels. To the right sits a single green control plane box that touches no request, with dashed green arrows pushing configuration into every proxy. The dark band states the division of labour: if the brain goes down, change stops but traffic keeps flowing, because the proxies do the work and one brain merely programs them.

What the Mesh Buys

With the anatomy in place, the features stop being magic. Each one is just something a proxy on both ends of every call can do, driven by config.

Policy as configuration, not as code. A retry budget, a timeout ladder, a breaker threshold: in the library era each was a code change times N services times L languages. In the mesh it is one document the control plane pushes to every proxy. The change that took a quarter takes a minute, and it is uniform, the same behaviour on every hop, no team forgotten.

Encryption between every service. Because a proxy sits at both ends of every call, the pair can speak mutual TLS: each side proves its identity with a certificate, and every east-west byte is encrypted, without any application knowing it happened. The control plane issues and rotates the certificates. This is the feature that makes compliance teams love meshes, and its full story, identities, rotation, what mutual actually buys, belongs to TLS & mTLS in Architecture.

Sight on every hop. Every call crosses two proxies, so every call is measured twice, for free, in the same format: rate, errors, latency, per source, per destination. The mesh cannot see inside your services, that story is the observability lessons', but between them it sees everything, uniformly, including the services whose teams never instrumented anything.

Traffic as a dial. Because the proxy picks the destination, config can say "send 5% of checkout traffic to v2". That dial is what Rolling, Blue-Green, Canary will turn; the mesh is where the dial physically lives.

Read the list again and notice what is not on it: nothing about your domain. The smart-pipes line from API Gateway & BFF survives contact with the mesh untouched: the proxies do mechanical work brilliantly, and the moment someone proposes putting a pricing rule in mesh config, the ESB is being rebuilt with better marketing.

The Drill: One Fix, Two Worlds

The argument for the mesh is an operational argument, so the only honest way to feel it is to operate both worlds.

Your fleet has six services in four languages, and security has ordered one fix everywhere: encrypt service-to-service traffic and cap retries with a budget. Roll it out twice. In the library world, patch each service yourself and watch what the fleet looks like mid-campaign, and where it never quite arrives. In the mesh world, write the policy once and watch the control plane deliver it. The price strip stays on screen both times, because the mesh's costs do not pause while its benefits demo.

Then the third act, the one that bites production teams: the fleet now has two layers that know how to retry. Decide who gets to, and watch what happens to one wobbling service when you choose wrong.

Roll one security fix across a polyglot fleet twice: once as library patches, once as a control-plane push. Then decide who gets to retry.

The Bill, Honestly

The mesh solves the grid, and it invoices you three ways. Anyone selling you a mesh without this section is selling, not architecting.

The hop tax. Every call now crosses two extra processes: out through your sidecar, in through theirs. Each crossing is small, fractions of a millisecond to single-digit milliseconds under load, but it is paid on every hop of every request, and Tail Latency: Living at p99 taught you what happens to small per-hop costs in deep call chains: the tail collects them.

The pod tax. The sidecar is a real process with real reservations, on the order of a couple tenths of a CPU and tens of megabytes of memory per instance. Multiply by a thousand pods and the mesh is quietly one of the larger services you run. It is also capacity you pay for at provisioning time, whether the proxy is busy or not.

The ops tax, the one that actually decides adoptions. A mesh is a distributed system you now operate in order to operate your distributed system. The control plane must be kept available. The mesh itself has versions, and upgrading a hundred proxies under live traffic is exactly the kind of campaign it was meant to abolish, now owned by the platform team. Debugging grows a new question, "was that the app or the sidecar?", and configuration acquires a fleet-wide blast radius: one bad push reaches every proxy at once, the config-shaped version of the mass death Service Discovery taught you to distrust.

And one trap deserves its own paragraph, because it is the mesh failure mode teams discover during an outage. The mesh can retry. Your application already retries, the caller's discipline taught it to. Configure both innocently, three attempts each, and a single logical call to a struggling service becomes nine physical attempts, invisibly, because neither layer knows the other exists. Stack a third layer, a client SDK, a database driver, and Retries, Budgets & Retry Storms runs the arithmetic to its conclusion: two retries at each of five layers is 3 to the fifth, 243 attempts hammering the service that most needs mercy. The rule from that lesson becomes a mesh commandment: retries live at exactly one layer, with a budget. If the mesh owns them, the app must not. Choose in config, write it down, and audit for the day someone adds a helpful retry loop anyway.

A receipt titled: the mesh invoices you three ways. Row one, the hop tax, draws a single call as app to proxy to network to proxy to app, with the two added proxy crossings marked plus-hop, charged on every call. Row two, the pod tax, opens a pod holding an app and its proxy with the price tag of roughly 0.2 vCPU and 60 megabytes reserved per instance, busy or idle, charged on every pod. Row three, the ops tax, lists keeping the brain up, rolling mesh versions, and the blast radius of one bad push, charged forever. The dark band totals the receipt: a distributed system to run your distributed system.

Do You Need One? And the Pendulum

Now the judgment call, because the mesh is the most oversold pattern in this course's half of the industry.

The mesh pays for itself when the grid is real: many services, more than one language, many teams, and a hard requirement like fleet-wide mTLS that would otherwise be a campaign. Kill any leg of that and the case weakens fast. One language? A well-owned shared library gives you most of the value with none of the hop, pod, or ops taxes; the polyglot tax you'd be escaping is zero. A dozen services and one team? The campaign you are abolishing is an afternoon. No compliance driver? mTLS everywhere is a want, not a need. The honest sequence is the one this course keeps teaching: feel the tax first, then buy the machinery that removes it.

And do not confuse the mesh with the door you already own. The gateway governs north-south traffic at one entrance; the mesh governs east-west traffic on every hop. A fleet fronted by a gateway with no mesh is common and fine. A mesh behind a gateway is the full picture. Neither replaces the other.

One more swing of the pendulum, because the sidecar itself is now being renegotiated. The per-pod proxy's costs pushed the meshes toward sidecarless designs: a shared secure tunnel per node handling encryption and L4 duty for every pod on it, with a full proxy inserted only where L7 policy is actually needed. Half the hops, a fraction of the reserved capacity. Whether the proxy lives per-pod, per-node, or inside the kernel, squint and the pattern has not moved: cross-cutting concerns live outside application code, owned by the platform, programmed by a control plane. The packaging keeps changing. The separation is the point.

Diagram titled: each layer retries times three, nobody knows. Four stacked bands show the multiplication as actual dots: one request becomes three attempts in the app-retries band, nine in the mesh-retries band, and twenty-seven in the driver-retries band, with a times three arrow between each. All twenty-seven land on a red box labelled the wobbling service. The dark band states the commandment: retries live at one layer, with a budget.

Takeaways

  • The grid is the enemy: concerns times languages. The same networking duties re-implemented in every language of your fleet, kept identical by hope.
  • Libraries pay two taxes. Every new language re-implements the discipline, and every fix redeploys the world. The cure rhymes with the BFF's: move it out, give it one owner.
  • The sidecar is a change of address. The caller's discipline moves out of the process and into a proxy beside it. Language independence and platform-owned upgrades fall out of the address change.
  • A mesh is sidecars plus a brain. The data plane touches every request; the control plane touches none and programs them all. Control plane down means change stops, never traffic. You learned that law at the registry.
  • Its features are one trick. A proxy at both ends of every call, driven by config: uniform policy, mTLS, per-hop sight, traffic dials. Nothing domain-shaped; smart endpoints, dumb pipes still rules.
  • The bill is three taxes and a trap. Two hops on every call, reservations on every pod, a distributed system to run your distributed system, and retries that silently stack 3 times 3. Retries live at one layer, with a budget.
  • Buy it when the grid is real. Polyglot, many teams, mTLS mandates. One language and a dozen services? A library is honest engineering, not a compromise.
  • The packaging is renegotiable; the separation is not. Per-pod, per-node, in-kernel: cross-cutting stays out of your code, under a control plane.

The mesh moved your networking code out of the services. But look at what it manages with: configuration, pushed at runtime, versioned apart from the applications it steers. Your services are full of the same stuff, connection strings, feature toggles, tuning knobs, still baked into their builds. Getting config out of the build and into the runtime is its own pattern with its own traps: Externalized Configuration is next.