Chaos Engineering
Best practices for systematically injecting failures to discover weaknesses before they cause outages, using steady-state hypotheses, controlled experiments, and progressive blast radius expansion.
Chaos Engineering Principles
Chaos engineering is not random destruction. It is disciplined experimentation on distributed systems to build confidence in their resilience.
Define Steady State --> Form Hypothesis --> Design Experiment --> Control Blast Radius --> Run --> Analyze --> Fix --> Repeat
| Principle | Description | Why It Matters | |-----------|-------------|----------------| | Define steady state | Identify measurable normal behavior (latency, error rate, throughput) | Without a baseline, you cannot detect degradation | | Hypothesize around steady state | Predict the system will maintain steady state during fault | Forces explicit thinking about expected behavior | | Vary real-world events | Inject failures that actually happen (network, disk, process, dependency) | Simulated failures must map to real failure modes | | Run in production | Test where real complexity exists (with safeguards) | Staging rarely matches production topology | | Minimize blast radius | Start small, expand gradually, have kill switches | Chaos should reveal problems, not cause outages | | Automate experiments | Repeatable experiments run in CI/CD or on schedule | Manual experiments don't scale and introduce bias | | Build a hypothesis backlog | Track what you want to test and what you've learned | Systematic coverage prevents blind spots |
Steady-State Hypothesis Template
Every chaos experiment begins with a hypothesis. This template ensures experiments are structured and measurable.
# Steady-State Hypothesis Document
experiment:
name: "Payment service database failover"
id: "CHAOS-042"
date: "2026-02-07"
owner: "team-payments"
reviewer: "sre-team"
steady_state:
description: "Payment service processes transactions within SLO"
metrics:
- name: "p99 latency"
source: "prometheus"
query: 'histogram_quantile(0.99, rate(payment_request_duration_seconds_bucket[5m]))'
threshold: "< 500ms"
- name: "error rate"
source: "prometheus"
query: 'rate(payment_request_errors_total[5m]) / rate(payment_request_total[5m])'
threshold: "< 0.1%"
- name: "transaction throughput"
source: "prometheus"
query: 'rate(payment_transactions_total[5m])'
threshold: "> 100 tx/s"
hypothesis: >
When the primary database replica fails, the payment service will
failover to the secondary replica within 30 seconds, maintaining
p99 latency below 2 seconds and error rate below 1% during failover.
experiment_design:
action: "Kill primary PostgreSQL pod in payment-db StatefulSet"
duration: "5 minutes"
blast_radius: "payment namespace only"
rollback: "PostgreSQL operator will auto-recreate pod; manual failback if needed"
abort_conditions:
- "Error rate exceeds 5% for more than 60 seconds"
- "Total service outage detected (zero throughput for 30 seconds)"
- "Cascading failures detected in upstream services"
- "Any P1/P2 incident triggered by unrelated system"
expected_outcome:
- "Failover completes within 30 seconds"
- "p99 latency spikes to < 2s during failover, recovers to < 500ms"
- "Error rate stays below 1%"
- "No data loss or corruption"
actual_outcome: null # Filled after experiment
findings: null
action_items: null
Litmus Chaos Experiments
LitmusChaos Engine Manifest
LitmusChaos is a CNCF project for Kubernetes-native chaos engineering. The ChaosEngine connects your application to chaos experiments.
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: payment-service-chaos
namespace: payments
spec:
appinfo:
appns: payments
applabel: "app=payment-service"
appkind: deployment
engineState: active
chaosServiceAccount: litmus-admin
monitoring: true
# Steady-state checks before and after experiment
components:
runner:
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
experiments:
- name: pod-delete
spec:
probe:
# Steady-state verification probe
- name: "payment-api-health"
type: httpProbe
mode: Continuous
httpProbe/inputs:
url: "http://payment-service.payments.svc:8080/health"
insecureSkipVerify: false
method:
get:
criteria: "=="
responseCode: "200"
runProperties:
probeTimeout: 5s
interval: 10s
retry: 3
probePollingInterval: 2s
- name: "payment-latency-check"
type: promProbe
mode: Edge
promProbe/inputs:
endpoint: "http://prometheus.monitoring.svc:9090"
query: 'histogram_quantile(0.99, rate(payment_request_duration_seconds_bucket{namespace="payments"}[1m]))'
comparator:
type: float
criteria: "<="
value: "2.0"
runProperties:
probeTimeout: 10s
interval: 30s
retry: 2
components:
env:
- name: TOTAL_CHAOS_DURATION
value: "120"
- name: CHAOS_INTERVAL
value: "30"
- name: FORCE
value: "true"
- name: PODS_AFFECTED_PERC
value: "50"
Litmus Network Chaos Experiment
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: payment-network-chaos
namespace: payments
spec:
appinfo:
appns: payments
applabel: "app=payment-service"
appkind: deployment
engineState: active
chaosServiceAccount: litmus-admin
experiments:
- name: pod-network-latency
spec:
components:
env:
- name: TOTAL_CHAOS_DURATION
value: "180"
- name: NETWORK_LATENCY
value: "300" # 300ms added latency
- name: JITTER
value: "100" # 100ms jitter
- name: NETWORK_INTERFACE
value: "eth0"
- name: DESTINATION_IPS
value: "10.96.0.10" # Target specific dependency (e.g., database)
- name: CONTAINER_RUNTIME
value: "containerd"
- name: SOCKET_PATH
value: "/run/containerd/containerd.sock"
probe:
- name: "transaction-success-rate"
type: promProbe
mode: Continuous
promProbe/inputs:
endpoint: "http://prometheus.monitoring.svc:9090"
query: 'rate(payment_transactions_success_total{namespace="payments"}[1m]) / rate(payment_transactions_total{namespace="payments"}[1m]) * 100'
comparator:
type: float
criteria: ">="
value: "95.0"
runProperties:
probeTimeout: 10s
interval: 15s
retry: 3
Chaos Monkey Configuration
Netflix's Chaos Monkey randomly terminates instances in production. Modern implementations support Kubernetes and use Spinnaker integration.
# Chaos Monkey for Spring Boot (Simian Army successor)
# application.yml
chaos:
monkey:
enabled: true
watcher:
controller: true
restController: true
service: true
repository: true
component: false
assaults:
level: 5 # 1 in 5 requests affected
latencyActive: true
latencyRangeStart: 1000 # 1 second
latencyRangeEnd: 5000 # 5 seconds
exceptionsActive: true
exception:
type: java.lang.RuntimeException
arguments:
- className: java.lang.String
value: "Chaos Monk