Status: stable
DOL is a domain-specific language for querying, observing, and analyzing Docker infrastructure. The language defines a rich, implementable core that can be parsed into a typed AST and executed by a Rust CLI.
DOL treats Docker as a live data system:
DOL improves the day-to-day workflow where users otherwise combine docker ps, docker stats, docker events, shell scripts, Prometheus queries, and dashboard tools. The language does not replace Prometheus or Grafana; it provides a Docker-native query surface that can later export to those systems.
The language prioritizes:
DOL has nine top-level query families.
observeobserve reads the current state of Docker entities and optionally enriches containers with current metrics.
Execution mode: batch snapshot by default, hybrid when metric windows are requested.
Simple queries:
observe containers
observe containers where status = running
observe containers | where image contains "postgres" | select name, status, ports
Cross-target JOIN:
A JOIN clause merges rows from two targets on a matching key:
observe containers join images on image = repository
observe containers join networks on name = name
observe containers join volumes on scope = scope
Syntax:
observe <left-target> join <right-target> on <left-key> = <right-key> [ | <pipeline> ]
join.c. for containersi. for imagesn. for networksv. for volumesExecution: nested-loop join over all matching right-target rows for each left-target row; equality comparison via =.
Examples:
observe containers join images on image = repository + ":" + tag
observe containers join networks on id = id
observe containers join images on id = id | select c.name, i.repository
observe containers join images on id = id | where c.image = "nginx:latest"
observe containers join volumes on state = scope
eventsevents subscribes to Docker event streams or reads historical events when a time range is present.
Execution mode: stream by default, historical when last, from, or to is present and a telemetry store is configured.
Examples:
events containers
events containers where action = "die"
events containers | where action = "restart" | select time, container, image
inspectinspect reads a detailed entity snapshot. With at, it reads a historical snapshot from storage.
Execution mode: batch for current inspection, historical for at.
Examples:
inspect container api-service
inspect image postgres:16
inspect container api-service at "2026-01-01 12:00:00"
logslogs retrieves log output from a running container. Returns lines with line numbers, message content, and container name.
Execution mode: batch by default; streaming when a pipeline is present. In streaming mode (follow: true), logs are delivered in real-time until Ctrl+C or --timeout is reached. Pipeline nodes (where, select, limit) apply to the live stream.
Examples:
logs container my-app
logs container my-app tail 50
logs container my-app | where message contains "error" | select line, message
pingping tests connectivity to the Docker daemon. Returns a status field (ok or error) and a human-readable message.
Execution mode: batch.
Examples:
ping
analyzeanalyze runs deterministic analysis over current or stored Docker telemetry.
Execution mode: batch or historical depending on time qualifiers.
Examples:
analyze containers find anomalies
analyze containers find restart_loops last 10m
analyze containers find dependencies
analyze containers find density
analyze containers find leaks
analyze containers find drift
analyze container api-service correlate events last 1h
Analysis types: - anomalies (default) — detects high CPU, memory pressure, restart loops, and unhealthy states. - correlate — finds containers sharing images and labels. - explain — produces a diagnostic signal summary for one or all containers. - dependencies — maps compose project, network, and volume relationships. - density — container distribution across images, states, and compose projects. - leaks — detects memory usage growth trends from historical metrics (requires --store). - drift — compares two telemetry snapshots to detect image, state, label, or restart count changes (requires --store).
fieldsfields returns the schema (field names and types) for a given entity type. This is useful for discovery and tooling integration.
Execution mode: batch.
Examples:
fields containers
fields images
fields networks
fields volumes
composecompose queries containers, networks, volumes, and other resources grouped under a Docker Compose project. It filters resources by the com.docker.compose.project label.
Execution mode: batch for most targets; streaming for logs and networks (real-time log and network event streams).
Syntax:
compose ls
compose <project>
compose <project> containers
compose <project> services
compose <project> networks
compose <project> volumes
compose <project> health
compose <project> images
compose <project> stats
compose <project> ps
compose <project> logs <service> [tail <n>]
compose <project> port <service> <port>
compose <project> config [services|networks|volumes]
compose <project> events
observe compose <project>
compose ls lists all Docker Compose projects with their container, network, and volume counts. Supported fields: project, containers, running, stopped, networks, volumes, status.containers — listing all containers in the compose project with their labels and metrics.containers explicitly to make the target clear: compose myapp containers.services, each row additionally shows the service field extracted from the com.docker.compose.service label, allowing pipeline operations like select service, name, state.networks, lists all Docker networks filtered by the compose project label. Supported fields: id, name, driver, scope, containers, labels.volumes, lists all Docker volumes filtered by the compose project label. Supported fields: name, driver, mountpoint, scope, labels.health, lists containers within the compose project with their service name and health status. The health field is extracted from the Docker inspect /State/Health/Status endpoint. Supported fields include all container fields plus service and health.images, lists images used by containers in the compose project. Supported fields: id, repository, name, tag, digest, size, service.stats, shows resource usage statistics for compose project containers. Supported fields: name, service, cpu, memory, memory_limit, memory_pct, network_rx, network_tx, disk_read, disk_write.ps, shows enhanced container status with service names. Supported fields: name, service, image, state, status, health, restart_count, ports.logs <service>, retrieves log output for a specific service. Supported fields: line, message, service, container.port <service> <port>, shows port mappings for a service. Supported fields: service, container, ports.config, inspects the Compose project configuration from running containers. Supported fields (services): name, image, state, status, ports, restart_count, health, depends_on. Supported fields (networks): name, driver, scope, containers. Supported fields (volumes): name, driver, mountpoint, scope.events, collects Docker events for the compose project (batch operation with pipeline support).networks (when a pipeline is present), streams real-time network events filtered by the compose project (requires streaming mode).observe compose <project> syntax is an alternative form that reads identically to other observe sub-queries.Examples:
compose ls
compose ls | where containers > 5 | sort by project asc
compose myapp
compose myapp services
compose myapp networks
compose myapp volumes
compose myapp health
compose myapp images
compose myapp stats | where cpu > 80% | select name, service, cpu
compose myapp ps | where state = "running" | select name, service, health
compose myapp logs api-service tail 50
compose myapp logs api-service tail 100 | where message contains "error"
compose myapp port api-service 8080
compose myapp config services
compose myapp config networks
compose myapp config volumes
compose myapp | where cpu > 80% | select name, cpu
compose myapp health | where health = "unhealthy" | select name, service, health
alert when compose_project = 'myapp' and cpu > 85% for 2m then print "High CPU"
alertalert defines a continuously evaluated condition and an action.
Execution mode: stream or scheduled evaluation loop.
Alert actions are executed in real time: - print: Outputs a formatted message to stdout. - webhook: Sends an HTTP POST to the specified URL. Requires network access. - restart: Restarts the target container via the bollard native Docker API (restart_container()), without shelling out to the docker CLI.
When --store is active, all fired alerts are persisted to the alert_history table for audit and review.
Examples:
alert when cpu > 85% for 2m then print "High CPU"
alert when restart_count > 3 for 5m then print "Restart loop detected"
alert when memory > 90% for 1m then webhook "http://localhost:9000/hooks/docker"
alert when restart_count > 5 for 3m then restart container api-service
Top-level collection targets:
containersimagesnetworksvolumescompose <project> — dynamically scoped to containers within a Docker Compose projectSingular inspection targets:
container <name-or-id>image <name-or-id>network <name-or-id>volume <name-or-id>Target names are case-sensitive when they refer to Docker names or IDs. DOL keywords are lowercase in v0.2.
Common container fields:
idnameimagestatusstateportslabelscreated_atstarted_atfinished_atrestart_countcpumemorymemory_limitnetwork_rxnetwork_txdisk_readdisk_writecompose_projecthealth — health check status from Docker inspect (/State/Health/Status); null if no health check is configuredIndividual labels can be accessed with dot notation. For example, given a container with label com.docker.compose.project=myapp:
observe containers where label.com.docker.compose.project = "myapp"
observe containers | where label.env = "production" | select name, label.version
Common image fields:
idrepositorytagdigestsizecreated_atlabelsCommon network fields:
idnamedriverscopecontainerslabelsCommon volume fields:
namedrivermountpointscopelabelsCommon event fields:
time — event timestamp (Unix seconds, nanoseconds, or ISO 8601)type — event type (container, image, network, volume)action — action name (start, die, stop, restart, pull, etc.)actor_id — Docker entity ID for the event actorcontainer — container name (when applicable)image — image reference (when applicable)attributes — array of key=value strings from the event’s Actor AttributessetThe set pipeline node adds or overrides a field on each row. The value can be:
set tier = "prod"set mem_gb = memory / 1073741824 (arithmetic, function calls, field references)set health = if state = running then "up" else "down"set severity = case when cpu > 80% then "critical" else "ok" endExamples:
observe containers | set tier = "prod"
observe containers | set mem_gb = memory / 1073741824 | select name, mem_gb
observe containers | set label = upper(name) | select name, label
observe containers | set health = if state = running then "up" else "down"
observe containers | set severity = case
when cpu > 80% then "critical"
when cpu > 50% then "warning"
else "ok"
end | select name, severity
DOL v0.2 supports these literal types:
"api-service"running, api-service, postgres420.7585%30s, 5m, 1h, 2d"2026-01-01 12:00:00"true, falseBare identifiers are accepted for simple values in filters, but strings are recommended when a value contains punctuation, spaces, or mixed case.
DOL implements a static semantic analyzer and type safety validation pass before executing any query. This phase runs immediately after parsing and prevents execution of queries that contain structural or type errors:
select), and aggregations (group by) are checked against the static schema of the collection target (e.g., containers, images). Dynamically added fields via set, fill, and let are added to the active schema context and allowed in downstream pipeline nodes.label. prefix (e.g., label.env) are recognized and statically validated as long as the base labels field exists on the target.state) to an Integer literal (e.g., 50) with binary comparison operators like > will be rejected at compilation/validation time.AND, OR, and NOT operators require boolean operands.=!=><>=<=contains — substring matchmatches — regex match (Rust regex syntax)starts_with — prefix match: name starts_with "api-"ends_with — suffix match: image ends_with ":latest"in — set membership: expr in ("a", "b", "c")between ... and ... — numeric range check: cpu between 50 and 80is null — null check: finished_at is nullis not null — not-null check: finished_at is not null+ — addition- — subtraction (also unary minus: -5)* — multiplication/ — division% — moduloArithmetic expressions can be used in set assignments, where filters, having filters, and anywhere an expression is expected.
andornotString functions: - upper(s) — returns uppercase - lower(s) — returns lowercase - length(s) — returns Integer string length - trim(s) — returns trimmed whitespace - concat(a, b, ...) — returns string concatenation of all arguments - substring(s, start, len) — returns substring extraction - coalesce(a, b, ...) — returns first non-null, non-empty value - starts_with(s, prefix) — returns Boolean, true if s starts with prefix - ends_with(s, suffix) — returns Boolean, true if s ends with suffix - replace(s, from, to) — returns string with all occurrences of from replaced with to - reverse(s) — returns reversed string - repeat(s, n) — returns string repeated n times - position(s, substr) — returns Integer, 0-based index of first substr occurrence (0 if not found) - split_part(s, delim, n) — returns split by delim, the n-th part (1-indexed)
Type conversion functions: - to_int(value) — converts to Integer (parses strings, truncates floats, maps true/false to 1/0) - to_float(value) — converts to Float (parses strings, casts integers) - to_string(value) — converts any value to its string representation
Date/time functions: - now() — returns current UTC timestamp as RFC 3339 string - date_format(ts, fmt) — format a timestamp string according to fmt (strftime syntax, e.g., %Y-%m-%d) - date_diff(a, b, unit) — returns Integer difference between two timestamps in the given unit (seconds, minutes, hours, days) - extract(ts, part) — returns Integer part of a timestamp: year, month, day, hour, minute, second
The coalesce() function returns the first non-null, non-empty value from its arguments:
observe containers | set name = coalesce(label.name, name, "unknown") | select name
If all arguments are null or empty strings, coalesce() returns null.
$var Field ReferencesField names can be prefixed with $ for explicit field access. This is useful when a field name might otherwise be parsed as a literal value:
observe containers where $state = running
observe containers | set $cpu = cpu
$state is equivalent to state — the $ prefix is stripped during parsing and the result is treated as a field reference.
Precedence, highest to lowest:
( ... )-*, /, %+, -contains, matches, starts_with, ends_with, between, is null, is not nullnotandorExample:
observe containers where status = running and (cpu > 80% or memory > 90%)
observe containers where image in ("postgres", "mysql", "redis")
observe containers where name matches "^api-"
observe containers where cpu between 50 and 80
observe containers | where finished_at is not null | select name
observe containers | set mem_gb = memory / 1073741824 | select name, mem_gb
observe containers | where upper(name) contains "API" | select name
DOL v0.2 supports relative windows, point-in-time inspection, and full historical ranges.
Relative windows:
last 5m
last 1h
last 2d
Point-in-time:
at "2026-01-01 12:00:00"
Historical ranges are fully supported via the telemetry store:
from "2026-01-01 12:00:00" to "2026-01-01 13:00:00"
Time semantics:
observe reads the current Docker snapshot.events opens a live stream.last <duration> means the interval ending at query evaluation time.at <timestamp> means the nearest stored snapshot at or before that timestamp.Pipelines transform query output from left to right.
Supported pipeline nodes:
where <expression>select <field-list>group by <field-list> [with <agg>(<field>) as <alias> [, ...]]having <expression>sort by <field> [asc|desc] [, <field> [asc|desc] ...]limit <integer>offset <integer>distinctalert <string>set <field> = <value-expr>fill <field> with <set-value> [where <condition>] — Replace null/empty values in <field> with the result of <set-value>. The value supports if/case/when expressions (same as set). An optional where <condition> restricts which rows get filled.let $name = <expr> — Declare a constant or parameter. The expression is evaluated and the result is added to each row as a field named $name (the $ prefix is optional). Useful for declaring thresholds, labels, or reusable values: let $threshold = 80debug — Print intermediate row count and schema information to stderr.assert <expression> — Validate that every row satisfies a condition. If any row fails, the query terminates with an error. Does not modify the data stream. Useful for understanding query execution, especially in complex pipelines: observe containers | debug | where cpu > 80% | debug | select name, cpuif <condition> then <pipeline-node> [else if <condition> then <pipeline-node>] [else <pipeline-node>]row_number as <alias> — Assign a sequential row number (1-based) to each row in the result set.rank(<field>) as <alias> — Rank rows by field value. Ties receive the same rank.lag(<field> [, <offset>]) as <alias> — Access the value of <field> from the previous row (offset defaults to 1). First rows without a preceding row yield null.lead(<field> [, <offset>]) as <alias> — Access the value of <field> from the following row (offset defaults to 1). Last rows without a following row yield null.Pipeline rules:
observe containers.where filters records.select changes output shape.group by aggregates records by field values. When with is given, the specified aggregate functions (sum, count, avg, min, max, median, percentile) are computed per group with optional as aliases. percentile(field, p) accepts an additional numeric argument for the percentile value (0–100).having filters groups after aggregation (similar to where but operates on aggregate values).sort by supports multiple sort fields with independent direction per field.limit stops after N records.offset skips the first N records.distinct removes duplicate rows (same values across all fields).alert inside a pipeline emits an alert when a record reaches that stage.set <field> = <value> adds or overrides a field on each row. The value can include arithmetic expressions, function calls, and field references.if <condition> then <nodes> [else <nodes>] conditionally applies nested pipeline nodes.Examples:
observe containers | where cpu > 80% | select name, cpu, memory
observe images | sort by size desc | limit 10
events containers | where action = "die" | group by image
observe containers | set health = if state = running then "healthy" else "unhealthy"
observe containers | if cpu > 90% then alert "Critical CPU" else alert "OK"
observe containers | set severity = case when cpu > 80% then "critical" else "ok" end
observe containers | set mem_gb = memory / 1073741824 | select name, mem_gb
observe containers | sort by state desc, cpu desc | select name, state, cpu
observe containers | distinct | select image
observe containers | sort by name asc | offset 10 | limit 5
observe containers | group by image with count(id) as cnt | having cnt > 3
observe containers | group by image with avg(cpu) as avg_cpu | sort by avg_cpu desc
observe containers | group by image with median(cpu) as med_cpu
observe containers | group by image with percentile(cpu, 95) as p95_cpu
observe containers | fill memory with 0 | where memory > 500
observe containers | where name starts_with "api-"
observe containers | where image ends_with ":latest"
observe containers | set mem_gb = memory / 1073741824
observe containers | set today = now()
observe containers | set day = extract(created_at, 'day')
observe containers where $state = running
observe containers | let $threshold = 80 | where cpu > $threshold
observe containers | let $app = "myapp" | where compose_project = $app
Batch queries consume finite input and return finite output.
Batch examples:
observe containers
observe images | sort by size desc | limit 5
inspect container api-service
Batch execution steps:
Stream queries consume unbounded input and keep running until cancelled.
Stream examples:
events containers
events containers | where action = "die"
logs container my-app
logs container my-app tail 50 | where message contains "error"
compose myapp events (batch)
compose myapp events | where action = "die" (batch)
compose myapp networks
compose myapp networks | where action = "connect"
compose myapp logs service api
compose myapp logs service api | where message contains "error"
alert when cpu > 85% for 2m then print "High CPU"
Stream execution rules:
limit may terminate a stream after N matching records.sort by is invalid on unbounded streams unless a finite window is present.for <duration>.Hybrid queries combine a current Docker snapshot with recent metric samples.
Examples:
observe containers last 5m
observe containers | where cpu > 80%
analyze containers find anomalies last 1h
Hybrid execution rules:
Historical queries require a telemetry store.
Examples:
inspect container api-service at "2026-01-01 12:00:00"
events containers last 1h
analyze container api-service correlate events last 1h
Historical execution rules:
at selects the nearest snapshot at or before the timestamp.last selects all matching records in the window ending at execution time.DOL v0.2 uses best-effort consistency:
The implementation should preserve timestamps and raw Docker IDs so users can reconcile output with Docker itself.
Parser errors should include:
Runtime errors should distinguish:
Examples:
parse error at column 28: expected field
--> observe containers | where | sort by cpu
^
parse error at column 20: expected expression after `where`
--> observe containers | where | sort by cpu
^
runtime error: `sort by` requires finite input; add `last 5m` or `limit`
runtime error: historical query requires a telemetry store
Parser errors show:
--> pointing to the offending query.^ pointer under the exact column where parsing failed.observe, events, inspect…” or “did you mean containers?”)In the CLI and REPL, error messages are displayed in ANSI red with bold formatting for visual prominence. Source pointers appear in cyan (-->), caret in green (^), and suggestions in yellow (help:).
Reserved keywords:
alert analyze and asc at between by case compose contains correlate count dashboard
assert debug config desc distinct else end events explain extract false fields fill find for from
group having health if in inspect is join last let limit logs matches max min networks
not null observe of offset or ping repl repeat restart select service services set sort
split_part starts_with ends_with sum then to top true upper lower length trim concat
substring coalesce replace reverse position now date_format date_diff volumes
webhook when where
Docker names that conflict with reserved keywords must be quoted as strings when used as values.
The parser and executor should prioritize these queries first:
observe containers
observe containers where status = running
observe containers | where image contains "postgres" | select name, status
observe images | sort by size desc | limit 10
events containers where action = "die"
events containers | where action = "restart" | select time, container, image
inspect container api-service
inspect container api-service at "2026-01-01 12:00:00"
analyze containers find anomalies
analyze containers find restart_loops last 10m
alert when cpu > 85% for 2m then print "High CPU" observe containers | where cpu > 80% | alert "High CPU detected"
Supported aggregate functions for group by ... with ...:
| Function | Syntax | Description | Return Type |
|---|---|---|---|
count |
count(field) |
Count of rows in each group | Integer |
sum |
sum(field) |
Sum of numeric values | Float |
avg |
avg(field) |
Arithmetic mean | Float |
min |
min(field) |
Minimum value | Float |
max |
max(field) |
Maximum value | Float |
median |
median(field) |
Median (50th percentile) | Float |
percentile |
percentile(field, p) |
The p-th percentile (0–100) using linear interpolation | Float |
Examples:
observe containers | group by image with median(cpu) as med_cpu
observe containers | group by image with percentile(memory, 95) as p95_mem
observe containers | group by image with avg(cpu) as avg_cpu, percentile(cpu, 99) as p99_cpu
These features are intentionally deferred:
The parser was implemented incrementally, starting with the core query families and gradually adding pipeline nodes. The current implementation supports all nine query families and all pipeline nodes described in this specification.
AST types (defined in src/ast.rs):
QueryTarget (CollectionTarget, SingularTarget, ComposeTarget)ExpressionValuePipelineNodeTimeSelectorAlertRuleJoinClauseSetValueAggregateExprThe DOL CLI supports the following flags:
| Flag | Description |
|---|---|
--store <path> |
Path to SQLite telemetry store |
--collect |
Start background data collection daemon |
--metrics-interval <s> |
Metrics polling interval in seconds (default: 30) |
--snapshot-interval <s> |
Snapshot interval in seconds (default: 300) |
--store-stats |
Display telemetry store statistics |
--apply-retention |
Apply retention policies to clean old data |
--output <fmt> |
Output format: table, json, json-compact, csv, jsonl (default: table) |
--export <path> |
Write output to file (format inferred from extension: .csv, .json, .jsonl, .table) |
--export-format <fmt> |
Export file format: influx, loki, prometheus (used with --export) |
--file <path> / -f <path> |
Read the DOL query from a .dol file |
--host <addr> |
Docker daemon address (e.g., tcp://192.168.1.100:2375) |
--watch <s> |
Re-run query every N seconds (batch and alert queries) |
--timeout <s> |
Query execution timeout in seconds — if a query takes longer than this, it is aborted (applies to watch, alert, events, store, and single queries) |
--explain |
Show the logical query plan without executing |
--diff |
Compare query results with the last store snapshot (requires --store) |
--theme <dark\|light> |
Color theme for table output (dark or light); can also be set permanently in config via theme: dark\|light |
--completion <shell> |
Generate shell completion script (bash, zsh, fish, powershell, elvish) |
--export-influx <url> |
Push results to InfluxDB v1/v2 HTTP write API |
--export-grafana-loki <url> |
Push results to Grafana Loki HTTP push API |
--export-prometheus <url> |
Push results to Prometheus Pushgateway |
repl |
Start interactive REPL shell with tab completion, history, and REPL commands |
top |
Live-updating TUI container monitor (top-like) with auto-refresh, keyboard controls, CPU/MEM gauge bars, filter mode, and event-driven refresh |
dashboard |
Multi-panel TUI dashboard with container list, state distribution stats (histogram bars), top images, and live Docker events stream |
config init |
Create a default config file at the standard config path |
config set <key> <value> |
Update a configuration value (store, output, host, metrics-interval, snapshot-interval, theme, api-timeout, api-quick-timeout, stats-timeout, events-timeout, webhook-timeout, restart-timeout) |
config view |
Display the current merged configuration (from CLI flags + config file + defaults) |
Additional REPL commands (within dol repl):
| REPL Command | Description |
|---|---|
.help |
Show available REPL commands |
.exit / .quit |
Exit the REPL |
.history |
Show command history |
.watch <secs> |
Re-run the last query every N seconds |
.export <path> |
Write subsequent results to a file |
.output <fmt> |
Set output format (table, json, json-compact, csv, jsonl) |
.host [<addr>] |
Show or set Docker host address within the REPL session |
Config file support (YAML/TOML):
~/.config/dol/config.yaml~/.config/dol/config.toml~/.dolrc.dolrcdol.yamldol.toml~/.dolrc.yaml~/.dolrc.tomlDOL uses configurable timeouts for all Docker API calls. These can be set in the config file or via dol config set:
| Config Key | Default | Description |
|---|---|---|
api-timeout |
30 |
Timeout (seconds) for standard Docker API calls (list containers, images, networks, volumes, inspect, logs). |
api-quick-timeout |
10 |
Timeout (seconds) for lightweight calls (ping). |
stats-timeout |
10 |
Timeout (seconds) for per-container stats collection. |
events-timeout |
30 |
Max seconds to wait for a single event from the Docker events stream (used by dol top, dol dashboard). |
webhook-timeout |
10 |
HTTP request timeout for alert webhook POST actions. |
restart-timeout |
30 |
Timeout (seconds) for alert container restart actions. |
Example config:
store: /path/to/telemetry.db
output: table
theme: light
api_timeout: 60
stats_timeout: 15
events_timeout: 60
webhook_timeout: 5
restart_timeout: 45All timeout values are optional — defaults are used when not set.
The grammar defined in this specification is the source of truth for parser tests.