References

Sidecar Pattern

Deploys auxiliary services (logging, monitoring, proxying, security) alongside main application, sharing lifecycle and resources. Common in container orchestration for microservices.


Use Cases

  • Service mesh (Envoy, Istio) - Traffic management, observability, mTLS
  • Logging aggregation - Centralized log collection (Fluentd, Filebeat)
  • Configuration management - Dynamic config updates (Consul, etcd)
  • Security - Auth proxy, TLS termination (oauth2-proxy)
  • Monitoring - Metrics collection (Prometheus, Datadog agent)

Core Patterns

✅ REQUIRED: Shared Lifecycle

Sidecar and main service start and stop together.

# Docker Compose - shared lifecycle
version: "3.8"
services:
  order-service:
    build: ./order-service
    ports:
      - "3000:3000"
    environment:
      LOG_ENDPOINT: http://log-sidecar:9000
    depends_on:
      - log-sidecar

  log-sidecar:
    image: fluentd:latest
    ports:
      - "9000:9000"
    volumes:
      - ./logs:/fluentd/log
      - ./fluentd.conf:/fluentd/etc/fluentd.conf

✅ REQUIRED: Shared Resources (Not Code)

Sidecars share network and storage, NOT code. Main service communicates via HTTP/gRPC to sidecar.

// ✅ CORRECT: Sidecar logger via HTTP endpoint
export class SidecarLogger implements ILogger {
  private readonly endpoint: string;

  constructor() {
    this.endpoint = process.env.LOG_ENDPOINT || 'http://localhost:9000';
  }

  async info(message: string, metadata?: Record<string, unknown>): Promise<void> {
    await fetch(`${this.endpoint}/logs`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ level: 'info', message, metadata, timestamp: new Date().toISOString() }),
    });
  }

  async error(message: string, metadata?: Record<string, unknown>): Promise<void> {
    await fetch(`${this.endpoint}/logs`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ level: 'error', message, metadata, timestamp: new Date().toISOString() }),
    });
  }
}

// ❌ WRONG: Importing sidecar code directly
import { FluentdLogger } from 'fluentd-sdk'; // Tightly coupled

Implementation Examples

Kubernetes Pod with Sidecars

apiVersion: v1
kind: Pod
metadata:
  name: order-service-pod
  labels:
    app: order-service
spec:
  containers:
    # Main container
    - name: order-service
      image: order-service:latest
      ports:
        - containerPort: 3000
      volumeMounts:
        - name: shared-logs
          mountPath: /var/log/app

    # Sidecar: log aggregator
    - name: log-aggregator
      image: fluentd:latest
      volumeMounts:
        - name: shared-logs
          mountPath: /var/log/app
          readOnly: true

    # Sidecar: auth proxy
    - name: auth-proxy
      image: oauth2-proxy:latest
      ports:
        - containerPort: 4180
      env:
        - name: UPSTREAM
          value: "http://localhost:3000"

  volumes:
    - name: shared-logs
      emptyDir: {}

Docker Compose with Multiple Sidecars

version: "3.8"
services:
  # Main application
  order-service:
    build: ./order-service
    ports:
      - "3000:3000"
    depends_on:
      - log-sidecar
      - metrics-sidecar

  # Logging sidecar
  log-sidecar:
    image: fluentd:latest
    ports:
      - "9000:9000"
    volumes:
      - ./logs:/fluentd/log

  # Monitoring sidecar
  metrics-sidecar:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml

  # Security sidecar (optional)
  auth-sidecar:
    image: oauth2-proxy:latest
    ports:
      - "4180:4180"
    environment:
      UPSTREAM: "http://order-service:3000"

Service Mesh (Istio)

# Istio automatically injects Envoy sidecar proxy
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  labels:
    app: order-service
spec:
  template:
    metadata:
      labels:
        app: order-service
      annotations:
        sidecar.istio.io/inject: "true"  # Envoy sidecar auto-injected
    spec:
      containers:
        - name: order-service
          image: order-service:latest
          ports:
            - containerPort: 3000

Decision Tree

Need cross-cutting concerns across microservices?
→ No: Use middleware or in-process libraries
→ Yes: Continue

Running in container orchestration (K8s, Docker)?
→ No: Consider in-process alternatives
→ Yes: Sidecar pattern is good fit

Which concern?
  Logging        → Fluentd/Filebeat sidecar
  Monitoring     → Prometheus/Datadog agent sidecar
  Auth proxy     → oauth2-proxy sidecar
  Traffic mgmt   → Envoy/Istio service mesh
  TLS/mTLS       → Istio/Linkerd service mesh
  Config mgmt    → Consul/etcd sidecar

Resource constraints?
→ Tight: Minimize sidecars, combine functionality
→ Flexible: Use specialized sidecars per concern

Advantages

  • Decouples auxiliary functionality from main service code
  • Shares resources (network, storage) without code coupling
  • Independent deployment and scaling of sidecars
  • Technology-agnostic (sidecar can use different language/runtime)
  • Reusable across services (same sidecar image)
  • Consistent cross-cutting concerns across all services

Disadvantages

  • Increased resource consumption (each sidecar uses CPU/memory)
  • More complex orchestration (more containers to manage)
  • Potential resource contention (shared CPU/memory limits)
  • Network latency (inter-container communication, though minimal)
  • Debugging complexity (more moving parts to troubleshoot)

When to Use

  • Microservices architecture with shared cross-cutting concerns
  • Need to add functionality without modifying main service
  • Consistent logging/monitoring across services
  • Service mesh deployment (Istio, Linkerd)
  • Need to proxy or filter traffic to/from service

When NOT to Use

  • Monolithic applications (use middleware instead)
  • Simple services without cross-cutting concerns
  • Resource overhead is unacceptable
  • Serverless environments (Lambda, Cloud Functions)
  • Single-container deployments

  • Ambassador pattern - Sidecar that proxies outbound requests to external services
  • Adapter pattern - Sidecar that normalizes output formats (metrics, logs)
  • See backend-integration.md for complete backend examples
  • See hexagonal-architecture.md for port/adapter pattern (conceptually similar)
  • See dry-principle.md for extracting shared cross-cutting concerns

References