Skip to content
Back to skills

Chaos Test

BSecurity

Inject failures (network, latency, crashes) against a running service to verify resilience. Run after adding an external dependency (cache, queue, third-party API), before promoting a service to a higher SLO tier, or before a high-traffic event. Requires a running service — this is not a static resilience review.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 20, 2026
ai-agentsgobashdockerkubernetesawsapi

Works with

  • cli
  • api

Security analysis

B75/100
  • criticalAccesses sensitive system or user directories

Pro shows the line behind each finding and how to fix it

Scanned October 20, 2026

npx -y skills add adnanmokhtar/refract --skill chaos-test --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Chaos Test?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Chaos Test
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/adnanmokhtar-chaos-test/badge)](https://www.skillsdirectory.com/skills/adnanmokhtar-chaos-test)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: chaos-test
description: Inject failures (network, latency, crashes) against a running service to verify resilience. Run after adding an external dependency (cache, queue, third-party API), before promoting a service to a higher SLO tier, or before a high-traffic event. Requires a running service — this is not a static resilience review.
kind: skill
pack: distributed-systems
allowed-tools: [Read, Grep, Glob, Bash]
---

# chaos-test

Prove resilience by breaking dependencies on purpose. Unit tests don't cover "Redis is down for 60 seconds".

## Premise

Real signals only. Every "PASSED" / "FAILED" cites a measured baseline + during-chaos + after-recovery comparison from the actual run. Hypothesis stated upfront with concrete numbers (error rate, p99). The fault injection cites the exact tool command (`toxiproxy-cli toxic add ...`, or `kubectl apply -f ...` under the named kubeconfig of § Target safety). Root cause cites `<path:line>` of the code that failed under contention. No "should be resilient" conclusions — either the metrics show it held, or they don't.

**Local first.** The default target is a throwaway stack on this machine: the service's dependencies in Testcontainers (or `docker run`) with Toxiproxy between the service and each dependency. A shared cluster, a staging account or a SaaS chaos tool is used only when the user names it in this session.

## Target safety (read first)

The machine's default kube context may point at a real managed cluster, and its default cloud profile at a real account. A chaos command that reaches either is an incident, not an experiment.

- **Every Bash block that may run `kubectl`, `helm` or a chaos-mesh CLI starts with this line**, before anything else in the block:
  ```bash
  export KUBECONFIG="$CHAOS_KUBECONFIG"; [ -n "$CHAOS_KUBECONFIG" ] && [ -s "$CHAOS_KUBECONFIG" ] && [ "$(cd "$(dirname "$CHAOS_KUBECONFIG")" && pwd)/$(basename "$CHAOS_KUBECONFIG")" != "$HOME/.kube/config" ] && [ "$(kubectl config current-context)" = "$CHAOS_CONTEXT" ] || { echo "HALT: CHAOS_KUBECONFIG/CHAOS_CONTEXT unset, empty, the default file, or naming a context the user did not give"; exit 1; }
  ```
  `CHAOS_KUBECONFIG` is a kubeconfig file and `CHAOS_CONTEXT` a context name, **both given by the user in this session** (a throwaway kind cluster's kubeconfig in a scratch folder qualifies). `kubectl config current-context` only reads that file.
- **Halt** — run nothing on a cluster — when `CHAOS_KUBECONFIG` is unset or empty, when it is `~/.kube/config`, when its current context is not the `CHAOS_CONTEXT` the user named, or when the user named no context at all. Say which, and print the commands that would have run.
- **Never** rely on the default context, pass `--context` to switch into one the user did not name, or loop over binaries with `version` (some cluster tools contact the current context on any invocation).
- **Docker.** `docker`, `pumba` and Testcontainers act on the current Docker endpoint. Run `docker context show` first and halt unless it is the local engine (`default`, `desktop-linux`, `colima` or `orbstack`) with `DOCKER_HOST` unset.
- **Cloud.** No default cloud profile and no real credentials: a cloud-side experiment (Gremlin, AWS FIS) runs only against an account and profile the user names, and is out of scope otherwise.
- Starting a VM or Docker engine (colima, Docker Desktop) is a side effect: say so in the report, and stop what this run started.

## Halt conditions

- Refuse to declare "resilient" without baseline + during-chaos + recovery numbers captured.
- Refuse to run chaos in prod without explicit window + customer comms confirmed.
- Halt on every § Target safety condition: no named kubeconfig and context for a cluster step, a non-local Docker endpoint, an unnamed cloud account.
- Halt if synthetic load is < 10% of prod RPS — failure modes won't surface.
- Don't claim a fix works without re-running the same experiment after the patch.
- Always verify cleanup of chaos rules (`toxiproxy-cli list`; on a named cluster, `kubectl get networkchaos` behind the § Target safety line) after each run.

## When to use

- After adding a new external dependency (cache, queue, third-party API).
- Before promoting a service to a higher SLO tier.
- Before a high-traffic event (sale, campaign, launch).
- After incidents to verify the fix actually closes the failure mode.

## Prerequisites

- **Default:** the service running locally against its real dependencies in containers — Testcontainers in the project's test stack, or `docker run` / compose — with Toxiproxy (`ghcr.io/shopify/toxiproxy`, v2.12.0 on 2025-03-18) in front of each dependency, plus a traffic generator. Testcontainers ships a Toxiproxy module for the major languages; read its package name in the registry before installing it.
- **Optional, only when the user names the target:** a staging environment with realistic data, or a cluster reached through the § Target safety kubeconfig.
- One of these chaos tools installed:
  - `toxiproxy` (`brew install toxiproxy` — homebrew-core; the old `shopify/shopify` tap path is retired) — TCP-layer faults. The local default.
  - `pumba` — Docker-container chaos (kill, pause, netem) on the local engine.
  - `chaos-mesh` — Kubernetes-native chaos (network partitions, pod kills), on a named kubeconfig only.
  - `gremlin` — SaaS chaos with prod-grade safety, on a named account only.
- Observability: metrics dashboard (Grafana/Datadog) + structured logs to confirm hypotheses.
- Approved blast-radius limits + a "stop button" (revoke the chaos rule).

## Procedure (one experiment at a time)

1. Pick **one** hypothesis: "If Redis is unavailable for 60s, the app serves cached data from DB and error rate stays < 1%."
2. Define success criteria — concrete numbers: error rate < 1%, p99 < 2x baseline, no page errors visible to user.
3. Stand up the local target (skip when the user named a staging or cluster target), and point the service at the proxy port instead of the dependency:
   ```bash
   docker context show   # halt unless local (§ Target safety)
   docker network create chaos-net
   docker run -d --name chaos-redis --network chaos-net redis:7-alpine
   docker run -d --name chaos-toxiproxy --network chaos-net -p 8474:8474 -p 26379:26379 ghcr.io/shopify/toxiproxy:2.12.0
   toxiproxy-cli create -l 0.0.0.0:26379 -u chaos-redis:6379 redis
   ```
4. Capture baseline (5 minutes of synthetic load with no chaos):
   ```bash
   k6 run --duration 5m --vus 50 load.js | tee baseline.txt
   ```
5. Inject the failure with a precise scope:
   ```bash
   # toxiproxy (local default): cut Redis for 60s
   toxiproxy-cli toxic add redis -t timeout -a timeout=0 --downstream
   sleep 60
   toxiproxy-cli toxic remove redis -n timeout_downstream

   # pumba (local engine only): kill one container at random
   pumba kill --signal SIGKILL re2:^api-
   ```
   On a cluster the user named, chaos-mesh runs in its own block, which starts with the § Target safety line:
   ```bash
   export KUBECONFIG="$CHAOS_KUBECONFIG"; [ -n "$CHAOS_KUBECONFIG" ] && [ -s "$CHAOS_KUBECONFIG" ] && [ "$(cd "$(dirname "$CHAOS_KUBECONFIG")" && pwd)/$(basename "$CHAOS_KUBECONFIG")" != "$HOME/.kube/config" ] && [ "$(kubectl config current-context)" = "$CHAOS_CONTEXT" ] || { echo "HALT: CHAOS_KUBECONFIG/CHAOS_CONTEXT unset, empty, the default file, or naming a context the user did not give"; exit 1; }
   kubectl apply -f experiments/redis-partition.yaml
   ```
6. Run the same load profile during the experiment.
7. Compare metrics during the window: error rate, p50/p95/p99, retry counts, queue depths, DB pool usage, log-level mix.
8. Record the verdict — hypothesis HOLDS or FAILED — and either close out or file a fix.
9. Clean up: `toxiproxy-cli list` shows no toxics; `docker rm -f chaos-toxiproxy chaos-redis && docker network rm chaos-net` for what step 3 started; on a named cluster, `kubectl get networkchaos` (behind the § Target safety line) shows none.
10. Re-run after the fix to confirm.

## Experiment catalogue

**Network**
- 500ms added latency on DB calls → does p95 stay within SLO?
- 5% packet loss on external API → do retries kick in, or does it hang?
- Full disconnect for 60s → does circuit breaker open + recover?

**Dependencies**
- Kill Redis → cache fallback works? error rate stays low?
- Kill one DB replica → traffic fails over without read errors?
- Exhaust DB connection pool → graceful 503 or hard crash?

**Process**
- Random pod kill → in-flight requests drain or drop?
- OOM kill → auto-restart works + alerts fire?
- Deploy during load → zero-downtime verified?

## Output

```
Experiment: Redis outage for 60s (toxiproxy timeout)
Target: local (docker context default; chaos-redis behind chaos-toxiproxy:26379)
Hypothesis: error rate < 1%, p99 < 2x baseline (200ms), zero user-visible errors.

Baseline (5m, 50 VUs):     error 0.02%   p50 38ms   p95 88ms   p99 121ms
During chaos (60s):        error 18%     p50 410ms  p95 1.8s   p99 4.2s    FAILED
After recovery (5m):       error 0.04%   p50 41ms   p95 92ms   p99 130ms

Root cause:
  Fallback path calls DB without timeout; pool exhausted under retry storm.

Action items:
  1. Add 200ms timeout on the DB fallback path (<modules-root>/cache/fallback.<ext>).
  2. Cap concurrent DB calls during the cache outage with a bulkhead.
  3. Re-run experiment after fix — must show error rate < 1%.
```

## False positives / gotchas

- Synthetic load that doesn't match prod traffic shape gives misleading results — record and replay real traces if possible.
- Chaos in dev with `--vus 5` tests almost nothing — failure modes appear under contention.
- Chaos tools often leak rules if killed mid-run — always verify cleanup with `toxiproxy-cli list`, and on a named cluster `kubectl get networkchaos` behind the § Target safety line.
- A kubectl line that "only reads" (`get`, `describe`) still reaches whatever context is current: it takes the § Target safety line like any other.
- Never run destructive chaos on prod without an explicit window + customer comms — even "safe" experiments have surprised teams.
- A single passing run isn't proof — re-run weekly. Drift in dependencies turns a green experiment red.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.