Deterministic Simulation Testing for containerised services.
Write Lua scripts to define, control, and verify chaos experiments on Docker containers with reproducible fault injection.
dst lets you write Lua scripts that define test subjects (Docker containers), inject faults (pause, kill, resource deprivation), and verify service resilience. All experiments are deterministic when seeded, making them reproducible across runs.
cargo install --path .Or build from source:
cargo build --releasecat examples/basic.lua | cargo runSets the random seed for deterministic fault selection. Must be called before dst.step() or dst.run_steps() when require_seed is true (default).
dst.seed(42)Creates a test subject container. Returns a subject ID string in the format docker/<container-id>.
local subject = dst.setup("docker", {
image = "kennethreitz/httpbin",
ports = { 80 },
})Makes an HTTP request to a subject. Returns a table with status and body.
local resp = dst.http(subject, "GET", "/get")
assert(resp.status == 200)Configures experiment behavior. Call before dst.step() or dst.run_steps().
dst.opts({
weights = {pause = 0.5, kill = 0.3, ["deprive:disk"] = 0.2},
accumulation = "single",
http_timeout = 10,
})Applies the next fault in the sequence. Returns a table describing what happened, or {more = false} when complete.
while true do
local result = dst.step()
if not result.more then
break
end
-- Verify service still works
local resp = dst.http(subject, "GET", "/get")
dst.info("status=" .. resp.status .. " after fault=" .. result.fault)
endReturn table fields:
fault(string): The fault type appliedsubject(string): The subject ID (format:docker/<container-id>)round(number): Current round number (1-indexed)total_rounds(number): Total rounds in this experimentremaining(number): Steps remainingmore(boolean): Whether more steps are available
Runs multiple steps and returns an array of results.
local results = dst.run_steps(5)
for i, result in ipairs(results) do
dst.info("round " .. result.round .. ": " .. result.fault)
endClears all active faults on a subject.
dst.clear(subject)dst.debug(msg)- Debug-level messagedst.info(msg)- Info-level messagedst.warn(msg)- Warning-level messagedst.error(msg)- Error-level message
dst.opts({
weights = {
pause = 0.40,
kill = 0.25,
["deprive:disk"] = 0.15,
["deprive:network"] = 0.15,
["deprive:memory"] = 0.05,
},
accumulation = "single",
http_timeout = 5,
http_retries = 30,
http_retry_delay = 500,
step_delay = 1000,
require_seed = true,
})| Option | Type | Default | Description |
|---|---|---|---|
weights |
table | See below | Fault type weights for weighted random selection |
accumulation |
string | "single" |
Fault accumulation mode |
http_timeout |
number | 5 |
HTTP request timeout in seconds |
http_retries |
number | 30 |
HTTP retry attempts |
http_retry_delay |
number | 500 |
Delay between HTTP retries (ms) |
step_delay |
number | 1000 |
Delay before applying fault in single mode (ms) |
require_seed |
boolean | true |
Require dst.seed() before stepping |
| Fault | Weight |
|---|---|
pause |
0.40 |
kill |
0.25 |
deprive:disk |
0.15 |
deprive:network |
0.15 |
deprive:memory |
0.05 |
single: Each subject can have only one active fault. Previous faults are cleared before applying new ones.accumulate: Multiple faults can stack on the same subject.
| Fault | Effect |
|---|---|
pause |
Pauses the container (SIGSTOP) |
kill |
Kills the container (SIGKILL) |
deprive:disk |
Throttles disk I/O to 1KB/s |
deprive:network |
Disconnects from bridge network (no internet) |
deprive:memory |
Limits container memory to 4MB |
Same seed produces identical fault sequences:
dst.seed(42)
local results1 = dst.run_steps(10)
dst.seed(42)
local results2 = dst.run_steps(10)
-- results1 and results2 have identical fault sequencesbasic.lua- Simple setup and HTTP checksadvanced.lua- Custom weights, stepping with verificationcoroutine.lua- User-controlled async-style execution with Lua coroutines
- Docker daemon running
- Rust 1.85+ (uses 2024 edition)
MIT