A CLI tool that turns Buildkite build URLs into waterfall timeline charts, helping you spot what's slow in your CI pipeline.
go install github.com/amrmahdi/bkslow/cmd/bkslow@latestdocker pull ghcr.io/amrmahdi/bkslowgit clone https://github.com/amrmahdi/bkslow.git
cd bkslow
make dockergit clone https://github.com/amrmahdi/bkslow.git
cd bkslow
make installbkslow needs a Buildkite API token with read_builds and read_job_logs permissions. It looks for a token in this order:
--tokenflagBUILDKITE_API_TOKENenvironment variable~/.config/bk.yaml(Buildkite CLI config, created bybk configure)
If you already use the Buildkite CLI, bkslow picks up your token automatically -- no extra setup needed.
Pass a Buildkite build URL to get a waterfall chart of all jobs:
bkslow "https://buildkite.com/myorg/mypipeline/builds/123"Pass a job-specific URL to get a breakdown of what happened inside that job:
bkslow "https://buildkite.com/myorg/mypipeline/builds/123#job-id"
bkslow "https://buildkite.com/myorg/mypipeline/builds/123/steps/canvas?sid=step-id"# Pass token via environment variable
docker run --rm -t -e BUILDKITE_API_TOKEN \
ghcr.io/amrmahdi/bkslow "https://buildkite.com/myorg/pipeline/builds/123"
# Mount your Buildkite CLI config so bkslow picks up the token automatically
docker run --rm -t -v ~/.config/bk.yaml:/home/nonroot/.config/bk.yaml:ro \
ghcr.io/amrmahdi/bkslow "https://buildkite.com/myorg/pipeline/builds/123"
# Pass token explicitly
docker run --rm -t ghcr.io/amrmahdi/bkslow --token tok_xxx \
"https://buildkite.com/myorg/pipeline/builds/123"| Flag | Short | Default | Description |
|---|---|---|---|
--token |
-t |
Buildkite API token (or set BUILDKITE_API_TOKEN) |
|
--sort |
-s |
start |
Sort jobs by start, name, or duration |
--jobs |
script |
Show script jobs only or all (includes waits, triggers) |
|
--hide-idle |
false |
Hide zero-duration jobs (skipped, blocked, waiting) | |
--json |
-j |
false |
Output as JSON instead of chart |
--raw |
-r |
false |
Dump full log (ANSI-stripped) instead of chart |
--waterfall |
false |
Force waterfall mode even when job ID is present | |
--width |
-w |
0 |
Terminal width override (0 = auto-detect) |
--name-width |
40 |
Max name column width | |
--threshold |
0 |
Hide entries shorter than this (e.g. 5s, 1m) |
Use --tui to launch an interactive terminal UI:
bkslow --tui "https://buildkite.com/myorg/pipeline/builds/123"The TUI lets you browse the build waterfall, drill into jobs, and inspect log sections.
Navigation:
| Key | Action |
|---|---|
j/k or β/β |
Move cursor up/down |
PgUp/PgDn |
Page up/down |
Home/End |
Jump to top/bottom |
Enter or β |
Drill into job/section |
Esc or β |
Go back |
q |
Quit |
Search/Filter (waterfall and job detail views):
| Key | Action |
|---|---|
/ |
Start fuzzy search β list filters live as you type |
β/β |
Navigate filtered results while typing |
Enter |
Confirm filter |
Esc |
Cancel search and restore previous view |
n/N |
Cycle through filtered results (wraps around) |
Esc (with active filter) |
Clear filter |
Search (log view):
| Key | Action |
|---|---|
/ |
Search log content (highlights matches) |
n/N |
Jump to next/previous match |
Esc |
Clear search highlights |
Show the build waterfall, hiding anything under 10 seconds:
bkslow --threshold 10s "https://buildkite.com/myorg/pipeline/builds/456"Sort jobs by duration to find the slowest ones:
bkslow -s duration "https://buildkite.com/myorg/pipeline/builds/456"Drill into a specific job to see individual test/step timings:
bkslow --threshold 5s "https://buildkite.com/myorg/pipeline/builds/456#job-uuid"When you point bkslow at a specific job, it tries to parse the log into individual entries. It tries these strategies in order and uses the first one that matches:
-
Pytest -- Detects
path/test.py::test_namepatterns withPASSED/FAILED/SKIPPEDresults. Each test becomes a waterfall entry. -
Docker build -- Detects
#N [stage M/N] COMMANDand#N DONE Xslines fromdocker build --progress plain. Each build step becomes a waterfall entry. Parallel stages show as overlapping bars. -
Shell commands -- Detects
$ commandlines that Buildkite adds when executing each command in your script. Each command becomes a waterfall entry with duration computed from timestamps between consecutive commands. -
Group sections -- Falls back to Buildkite's built-in section markers (
~~~,---,+++). This is what you get when none of the above match.
-
Minimum step threshold: Docker and shell command parsing both require at least 2 matching entries to activate. A single docker step or a single
$ commandwill fall through to group section parsing. -
Timestamps required for shell commands: The shell command parser needs OSC or
[ISO timestamp]prefixed lines to compute durations. If your log has$lines but no timestamps, it falls back to group sections. -
Docker CACHED steps have no duration: Cached docker steps don't produce
DONElines, so they won't appear in the output. Use--threshold 0if you want to see very short steps. -
Name truncation: Job names are truncated to
--name-width(default 40 chars). Docker step commands and shell commands are truncated to 80 chars. -
Multi-org tokens: bkslow matches the org slug from the URL against
~/.config/bk.yaml. If the org isn't found, it falls back to theselected_orgentry. If you have multiple orgs configured viabk configure, the right token is selected automatically. -
Step ID resolution: When using
?sid=URLs, bkslow makes an extra API call to resolve the step ID to a job ID. If the step ID doesn't match any job in the build, it exits with an error. -
Running builds: For in-progress builds, running jobs use the current time as their end time, so the chart reflects the duration so far.
-
Soft failures: Jobs that passed but have
soft_failed=trueare shown as "SFAIL" in the waterfall. -
Docker terminal width: When running via Docker, terminal width auto-detection requires a TTY. Use
docker run -tor pass--widthexplicitly.