Sidecar Pattern in Microservices
Picture a motorcycle with a sidecar attached. The motorcycle does the main job, driving, while the sidecar adds extra capabilities, such as carrying a passenger or luggage, without changing the motorcycle itself. The Sidecar pattern in microservices works exactly like this.
This guide explains how sidecars work, why they are useful, and how to run them in Kubernetes, with Docker Compose and in a service mesh. It also covers the Ambassador and Adapter variations, sidecar-less alternatives, and best practices, with configuration and Java examples.
1. What Is the Sidecar Pattern?
The Sidecar pattern attaches a helper component, the sidecar, to a main application, the microservice, to extend or enhance it without changing the application’s code.
In technical terms, the sidecar is a separate process or container that runs right next to the main application: in the same Kubernetes pod, or on the same virtual machine. The two share the same life cycle: they are created, scaled and removed together. If the service scales to five instances, there are five sidecars as well.
+-------------------------- Pod / host ---------------------------+
| |
| [ Main service ] <--- localhost ---> [ Sidecar ] |
| business logic logging, metrics, |
| (the motorcycle) security, proxying |
| (the sidecar) |
+------------------------------------------------------------------+
2. Why Do We Need the Sidecar Pattern?
Every microservice needs the same supporting features: logging, metrics, security, configuration, retries. The Sidecar pattern moves these non-business responsibilities into a helper, so the service stays simple and focused:
| Reason | What it means |
|---|---|
| Separation of concerns | Business logic stays in the service; cross-cutting concerns such as logging, retries, security and metrics live in the sidecar |
| Consistency across services | Every service uses the same sidecar for monitoring or authentication, so behavior is the same everywhere |
| Language independence | The sidecar works the same for Java, Python, Go or .NET services, so no library is needed per language |
| Easier maintenance | Common features are updated in the sidecar image without changing or redeploying application code |
| Reliability and security | Sidecars can add traffic control, retries, circuit breaking and encryption to services that lack them |
3. How a Sidecar Works with Its Service
Because the main container and the sidecar run together in one unit, they share resources that make cooperation easy:
| Shared resource | How the sidecar uses it | Example |
|---|---|---|
Network (same localhost) | They talk over localhost, without leaving the machine | The sidecar scrapes http://localhost:8080/actuator/prometheus |
| Volumes (shared folders) | One writes files, the other reads them | The service writes logs to /var/log/app; the sidecar ships them |
| Life cycle | Started, scaled and stopped together | Scaling to 5 pods creates 5 sidecars |
| Resources (node CPU and memory) | Both are scheduled on the same machine | Requests and limits must cover both containers |
Communication over localhost is very fast and needs no service discovery: the sidecar is always at the same address.
4. A Practical Example: Metrics, Discovery and Logs
Consider a service that must report metrics, register with service discovery and send its logs to a central store.
4.1 Without a sidecar
The microservice code contains client logic for the metrics system, the discovery system and the log store. Every service, in every language, needs its own version of this code. It bloats the service and makes upgrades painful.
4.2 With a sidecar
The main service contains only business logic. A separate sidecar container handles the infrastructure work, and they talk through localhost or a shared volume.
| The main service… | The sidecar… |
|---|---|
| Writes logs to standard output or a shared folder | Collects the logs and ships them to a central store, such as Elasticsearch or Loki |
| Exposes a simple metrics endpoint | Reads the metrics and forwards them to the monitoring system |
| Calls other services through a local address | Handles discovery, load balancing, retries and encryption for those calls |
Reports its health on /actuator/health | Registers or deregisters the service in the registry based on that health |
5. A Sidecar in Kubernetes
In Kubernetes, a sidecar is simply a second container in the same pod. Here, a log shipper reads the order service’s log files from a shared emptyDir volume:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
# 1. The main application
- name: order-service
image: shop/order-service:2.1
ports:
- containerPort: 8080
env:
- name: LOGGING_FILE_NAME # Spring Boot: logging.file.name
value: /var/log/app/order-service.log
volumeMounts:
- name: app-logs
mountPath: /var/log/app
resources:
requests: { cpu: "500m", memory: "512Mi" }
# 2. The sidecar: ships the log files to the central log store
- name: log-shipper
image: fluent/fluent-bit:3.1
volumeMounts:
- name: app-logs
mountPath: /var/log/app
readOnly: true
resources:
requests: { cpu: "50m", memory: "64Mi" }
limits: { cpu: "100m", memory: "128Mi" }
volumes:
- name: app-logs
emptyDir: {} # shared folder that lives as long as the pod
Fluent Bit is configured, through its own configuration file, to read /var/log/app/*.log and send the entries to the log store. Scaling the deployment to 10 replicas automatically creates 10 log shippers, one per pod.
6. Native Sidecar Containers in Kubernetes
Ordinary sidecar containers start and stop at the same time as the main container, which can cause problems: the app may start before its proxy is ready, or the log shipper may stop before the last logs are sent. Recent Kubernetes versions offer native sidecar containers to fix this. A native sidecar is an init container with restartPolicy: Always:
spec:
initContainers:
- name: log-shipper
image: fluent/fluent-bit:3.1
restartPolicy: Always # turns this init container into a native sidecar
volumeMounts:
- name: app-logs
mountPath: /var/log/app
readOnly: true
containers:
- name: order-service
image: shop/order-service:2.1
volumeMounts:
- name: app-logs
mountPath: /var/log/app
| Classic sidecar | Native sidecar | |
|---|---|---|
| Starts | At the same time as the app | Before the app, and keeps running |
| Stops | At the same time as the app | After the app has stopped |
| In Jobs | Can keep a finished Job’s pod running forever | Stops automatically when the main container finishes |
7. What Changes in the Java Application
A sidecar removes code from your service, but the service must still cooperate with it. For a Spring Boot service, that usually means a few small settings:
spring:
application:
name: order-service
# Write logs where the log-shipper sidecar can read them
logging:
file:
name: ${LOGGING_FILE_NAME:/var/log/app/order-service.log}
# Expose health and metrics for sidecars and the platform
management:
endpoints:
web:
exposure:
include: health,prometheus
endpoint:
health:
probes:
enabled: true
For metrics, add micrometer-registry-prometheus, and Spring Boot publishes everything at /actuator/prometheus, ready for a monitoring agent or sidecar to collect. The business code does not change at all.
8. The Ambassador: A Sidecar for Outgoing Calls
The Ambassador pattern is a special kind of sidecar that acts as a local proxy for a service’s outgoing calls. The application calls the ambassador on localhost, and the ambassador handles the hard parts of talking to a remote system: TLS, authentication, retries, timeouts, circuit breaking and monitoring.
Example: the order service calls an external payment provider. Instead of building all that logic into the service, it calls an ambassador sidecar, such as an Envoy proxy configured for the provider:
package com.example.order.payment;
import java.math.BigDecimal;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
@Component
public class PaymentProviderClient {
private final RestClient restClient;
public PaymentProviderClient(RestClient.Builder builder) {
// The ambassador sidecar listens on localhost:9000.
// It adds TLS, the API key, retries and timeouts before
// forwarding the call to https://api.payments.example
this.restClient = builder.baseUrl("http://localhost:9000").build();
}
public ChargeResponse charge(String orderId, BigDecimal amount) {
return restClient.post()
.uri("/v1/charges")
.header("Idempotency-Key", orderId)
.body(new ChargeRequest(orderId, amount))
.retrieve()
.body(ChargeResponse.class);
}
public record ChargeRequest(String orderId, BigDecimal amount) {
}
public record ChargeResponse(String chargeId, String status) {
}
}
The Java code stays simple and contains no secrets: the API key lives only in the ambassador’s configuration. Services in other languages can use the same ambassador image, and switching providers, or adding retries, changes only the sidecar.
9. Sidecars Without Kubernetes
Sidecars are most common in Kubernetes, but the idea works anywhere two processes can share a host. With Docker Compose, a container can join another container’s network, so both share the same localhost:
services:
order-service:
image: shop/order-service:2.1
ports:
- "8080:8080"
volumes:
- app-logs:/var/log/app
log-shipper:
image: fluent/fluent-bit:3.1
network_mode: "service:order-service" # share order-service's localhost
volumes:
- app-logs:/var/log/app:ro
depends_on:
- order-service
volumes:
app-logs:
On virtual machines, the same pattern appears as a helper process or agent installed next to the application, for example a monitoring agent or a local proxy managed by systemd.
10. Sidecars in a Service Mesh
The most famous use of sidecars is the service mesh. Tools such as Istio and Linkerd add a small proxy sidecar, such as Envoy or the Linkerd proxy, to every pod. All network traffic in and out of the service passes through that proxy, which adds:
- Mutual TLS: encrypted, authenticated traffic between services.
- Retries, timeouts and circuit breaking, configured centrally.
- Load balancing and traffic splitting, such as canary releases.
- Metrics and traces for every call, without code changes.
With Istio, you do not even add the sidecar yourself. Label a namespace, and Istio injects the proxy into every new pod:
# Every pod created in the "shop" namespace now gets an Envoy sidecar
kubectl label namespace shop istio-injection=enabled
# Restart the deployment so its pods are recreated with the sidecar
kubectl rollout restart deployment/order-service -n shop
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: payment-service
spec:
hosts:
- payment-service
http:
- route:
- destination:
host: payment-service
timeout: 5s
retries:
attempts: 3
perTryTimeout: 2s
retryOn: 5xx,connect-failure
The order service keeps calling http://payment-service as before; the sidecars add timeouts and retries. Be careful not to retry in both the mesh and your Java code, or the attempts multiply. See the Retry pattern.
11. Common Use Cases
| Use case | What the sidecar does | Example tools |
|---|---|---|
| Service mesh proxy | Handles all service-to-service traffic: TLS, retries, load balancing, metrics | Envoy (Istio), Linkerd proxy |
| Log shipping | Reads logs from a shared volume and sends them to a central store | Fluent Bit, Fluentd, Vector |
| Metrics and tracing | Collects metrics and traces and forwards them | OpenTelemetry Collector, Prometheus exporters |
| Configuration and secrets | Fetches configuration or secrets and keeps local files up to date | Vault Agent, config reloaders |
| Authentication proxy | Checks logins or tokens before requests reach the app | OAuth2 Proxy |
| Ambassador | Local proxy for outgoing calls to external systems | Envoy, custom proxies |
| Adapter | Converts the app’s output into a standard format | Exporters that turn custom metrics into Prometheus format |
12. Sidecar vs Ambassador, Adapter and Shared Libraries
| Approach | What it is | Best for |
|---|---|---|
| Sidecar (general) | Any helper container running next to the main service | Adding capabilities without code changes |
| Ambassador | A sidecar that proxies the service’s outgoing calls | Calling external or complex remote systems |
| Adapter | A sidecar that translates the service’s output into a standard interface | Making legacy or third-party apps fit your monitoring or logging standards |
| Shared library | The same feature built into each service as code, such as Resilience4j or Micrometer | Single-language systems wanting no extra containers |
| Node agent (DaemonSet) | One helper per machine instead of per pod | Log and metrics collection at lower cost |
Libraries and sidecars are not enemies. A Java team may use Resilience4j and Micrometer inside the service, and a mesh sidecar for encryption and traffic control. Choose based on how many languages you use and how much you want to keep out of application code.
13. Sidecar-less Alternatives
Sidecars cost CPU and memory in every pod. With hundreds of pods, that adds up. Newer approaches reduce this:
- Node-level agents (DaemonSets): one log or metrics collector per machine instead of one per pod.
- Istio ambient mode: a shared, per-node proxy handles encryption and basic traffic, with optional per-service proxies only where advanced features are needed.
- eBPF-based networking, such as Cilium: some mesh features run inside the operating system kernel instead of a proxy.
- Proxyless gRPC: gRPC clients receive mesh configuration directly, with no proxy in between.
Sidecars remain the most flexible and widely supported option, but it is worth checking these alternatives for large clusters.
14. Key Benefits
- Decoupling and separation of concerns: the main service focuses purely on business logic, so it is simpler, cleaner and easier to test.
- Reusability: the same sidecar works with many services in different languages; the infrastructure logic is written once.
- Polyglot support: a Python service and a .NET service use exactly the same sidecar for discovery or metrics, with no client libraries to maintain per language.
- Independent updates: upgrade the sidecar image, for example for a security fix, without touching application code.
- Isolation: the sidecar runs as a separate process, with its own resource limits, so a bug in it does not have to crash the main service, and vice versa.
15. Challenges and Considerations
| Challenge | Why it matters | How to handle it |
|---|---|---|
| Complexity | Two or more components to deploy, configure and monitor per service | Use standard sidecar images and automatic injection |
| Latency | Proxy sidecars add a local hop to every request (usually well under a millisecond, but it adds up) | Measure it; avoid unnecessary proxy chains |
| Resource usage | Every sidecar uses CPU and memory in every pod | Set requests and limits; consider node agents or sidecar-less options |
| Tied life cycle | Sidecars scale with the service, even if they do not need to | Use node-level agents for work that does not need one copy per pod |
| Startup and shutdown order | The app may start before its proxy is ready, or the sidecar may stop too early | Use native sidecar containers |
| Debugging | Problems can be in the app, the sidecar, or between them | Check both containers’ logs; use the mesh’s tracing |
16. Best Practices
- Keep sidecars generic: no business logic, only reusable infrastructure features.
- Set resource requests and limits for every sidecar, so they cannot starve the main container.
- Use native sidecar containers where available, to get correct startup and shutdown order.
- Standardize sidecar images and versions across services, and update them centrally.
- Prefer automatic injection, as in a service mesh, over copying sidecar definitions into every deployment.
- Avoid duplicate features: do not retry or encrypt both in the sidecar and in the application.
- Monitor sidecars like any other component: health, resource use and errors.
- Question each sidecar: if a node agent or a small library would do the job, it may be cheaper.
17. Common Mistakes to Avoid
| Mistake | What goes wrong | Better approach |
|---|---|---|
| Putting business logic in a sidecar | Logic is split across two deployables and becomes hard to change | Keep business logic in the service |
| No resource limits on sidecars | A busy sidecar slows down or starves the main application | Set requests and limits |
| Retrying in both the mesh and the code | Retries multiply and overload struggling services | Choose one place to retry |
| Ignoring startup order | The app fails its first calls because the proxy is not ready yet | Use native sidecars or wait for the proxy |
| One sidecar per small task | Pods fill up with many helpers that each use resources | Combine tasks, or use node-level agents |
| Unpinned sidecar versions | An automatic update changes behavior in every service at once | Pin versions and roll out updates gradually |
18. Summary
The Sidecar pattern extends and enhances microservices without changing their code. A helper container runs next to each service, shares its network, storage and life cycle, and takes over cross-cutting concerns such as logging, metrics, configuration, security and traffic control. Variations such as the Ambassador, for outgoing calls, and the Adapter, for translating output, solve specific problems, and the service mesh applies the pattern to every service at once. Keep sidecars generic, give them resource limits, use native sidecar containers for correct start and stop order, and weigh them against node agents and libraries. Used well, sidecars let developers focus on business value while delegating complex operational concerns to reusable, standardized components. Continue with the API Gateway pattern to see how traffic enters the system before it reaches your services.
19. Frequently Asked Questions (FAQ)
Q1. What is the Sidecar pattern in simple words?
The Sidecar pattern means running a small helper program right next to each service, in the same pod or on the same machine. The helper handles common jobs, such as shipping logs, collecting metrics or encrypting traffic, so the service itself only contains business logic.
Like a motorcycle sidecar, it adds capabilities without changing the motorcycle.
Q2. Why is it called a sidecar?
The name comes from a motorcycle sidecar: a small car attached to the side of a motorcycle. It travels wherever the motorcycle goes and adds capacity, but the motorcycle works without it and is not changed by it.
In the same way, a software sidecar is deployed, scaled and removed together with its service, and adds features without modifying the service’s code.
Q3. How does the main service communicate with its sidecar?
- Over
localhost: both share the same network, so the service calls, for example,http://localhost:9000. - Through shared files: one writes to a shared volume, the other reads, as with log files.
- Transparently: in a service mesh, the sidecar intercepts network traffic automatically, so the service does not know it is there.
Q4. What is the difference between a sidecar and an ambassador?
An ambassador is a type of sidecar with a specific job: it is a local proxy for the service’s outgoing calls. The application calls the ambassador on localhost, and the ambassador handles TLS, credentials, retries and routing to the remote system.
“Sidecar” is the general term for any helper running next to the service, such as a log shipper, a metrics collector or an ambassador.
Q5. What is the adapter pattern?
An adapter is a sidecar that translates what the application produces into a standard format the rest of the system expects. For example, an old application writes metrics in its own format, and an adapter sidecar converts them into Prometheus format.
It lets you fit legacy or third-party applications into your monitoring and logging standards without changing them.
Q6. Do sidecars only work in Kubernetes?
No. Kubernetes pods make sidecars especially easy, but the pattern works anywhere two processes can share a host:
- Docker Compose, using
network_mode: "service:<name>"to sharelocalhost. - Virtual machines, with a helper process or agent installed next to the application.
- Other container platforms, such as Amazon ECS tasks with several containers.
Q7. What are native sidecar containers in Kubernetes?
Native sidecars are init containers with restartPolicy: Always. Unlike ordinary sidecar containers, they:
- Start before the main containers, so a proxy is ready when the app starts.
- Keep running while the app runs, and stop after it, so no logs or requests are lost.
- Do not keep a finished Job’s pod alive.
They are available in recent Kubernetes versions and solve the most common sidecar ordering problems.
Q8. How much overhead does a sidecar add?
- Resources: typically tens of millicores of CPU and tens of megabytes of memory per sidecar, more for busy proxies.
- Latency: a proxy sidecar adds a local hop per request, usually well under a millisecond.
Small for one pod, but multiplied by every pod in a large cluster. Measure it with your real workload, and set resource requests and limits accordingly.
Q9. Is a service mesh just a collection of sidecars?
Mostly, yes. A traditional service mesh has two parts:
- The data plane: a sidecar proxy in every pod that handles the traffic.
- The control plane: a central component, such as Istio’s istiod, that configures all those proxies.
Newer designs, such as Istio’s ambient mode, replace per-pod sidecars with shared per-node proxies, but the idea of a configurable data plane stays the same.
Q10. Should I use a sidecar or a library such as Resilience4j?
- Library: best when all services use one language, you want fine-grained control in code, and you want to avoid extra containers.
- Sidecar: best when services use many languages, when you want features enforced consistently without code changes, or when you cannot change the application.
Many teams combine both, for example Resilience4j for business-aware fallbacks and a mesh sidecar for encryption and metrics. Just avoid doing the same job, such as retries, in both places.
Q11. What happens if the sidecar crashes?
Kubernetes restarts the sidecar container on its own, without restarting the main application. The impact depends on the sidecar’s job:
- A log shipper crash may delay logs, but the app keeps working.
- A proxy sidecar crash can block the app’s network traffic until it restarts.
Monitor sidecars, give them enough resources, and use health checks so problems are noticed quickly.
Q12. Can a sidecar be written in a different language from the service?
Yes, and that is one of its main advantages. A Java service can use a sidecar written in Go, Rust or C++, such as Envoy or Fluent Bit. Because they communicate through standard protocols and files, the languages do not matter.
This lets a whole organization share one sidecar, regardless of which language each team uses.
Q13. How do I add a sidecar to every service without editing each deployment?
Use automatic sidecar injection. Kubernetes supports admission webhooks that modify pods as they are created:
- Service meshes, such as Istio and Linkerd, inject their proxy when a namespace or pod is labeled.
- Other tools, such as Vault Agent, inject sidecars based on pod annotations.
This keeps deployment files simple and ensures every service gets the same sidecar version.
Q14. Can sidecars improve security?
Yes, in several ways:
- Mutual TLS: mesh sidecars encrypt and authenticate traffic between services.
- Secrets handling: an agent sidecar fetches secrets, so the app never stores credentials in its image or code.
- Authentication proxies check tokens before requests reach the app.
- Ambassadors keep external API keys out of application code.
Q15. When should I not use a sidecar?
- When a node-level agent, one per machine, can do the job more cheaply, as for basic log collection.
- When a small, well-supported library in your only language is simpler.
- For very small systems, where the extra operational complexity is not worth it.
- For short-lived jobs, unless you use native sidecars that stop with the job.
Q16. How do I debug problems involving sidecars?
- Check both containers’ logs:
kubectl logs pod-name -c sidecar-name. - Check resource usage: a sidecar near its CPU or memory limit can slow everything down.
- In a mesh, use the mesh’s tools and traces to see where a request was delayed or rejected.
- Temporarily test without the sidecar in a non-production environment to see whether it causes the issue.
Q17. Does every pod need its own sidecar for logging?
Not necessarily. If your services log to standard output, a single node-level agent (a Kubernetes DaemonSet) can collect logs from every container on the machine, which uses far fewer resources.
A per-pod logging sidecar is useful when an application can only write log files, needs special parsing, or must send logs to a different destination than other services.
Q18. How should a sidecar's resources be sized?
Start from the sidecar vendor’s recommendations, then measure real usage under load. Set:
- Requests to the typical usage, so the scheduler reserves enough room.
- Limits to protect the main container from a misbehaving sidecar.
Remember that the pod’s total requests include all containers, which affects how many pods fit on each node.
Q19. What is the difference between a sidecar and an init container?
- A classic init container runs before the app starts, does a one-time job, such as preparing files or waiting for a database, and then exits.
- A sidecar runs alongside the app for its whole life, providing an ongoing service.
Kubernetes native sidecars combine the two: they are defined as init containers, but with restartPolicy: Always they keep running.
Q20. Can sidecars be used with serverless functions?
Not in the classic sense, because platforms such as AWS Lambda do not let you run your own containers next to a function.
Some platforms provide similar ideas instead: AWS Lambda extensions run alongside a function to collect telemetry or fetch secrets, and container-based serverless platforms, such as Google Cloud Run, support sidecar containers.