Configuration and CLI compatibility contract

Status: 1.0 compatibility contract. This reference retains the CLI/YAML choices approved for 0.0.80 on 2026-09-27. HTTP, metrics and journal contracts have their own references. See the release notes for the validation record and its limits.

Use full option names and explicit selectors in automation. Global options go before the subcommand; that subcommand’s options go after it:

monit-docker -c config.yml --name 'web-*' stats --rsc status
monit-docker -c config.yml check-config --output json
monit-docker -c config.yml run web-guard --dry-run

Configuration source and precedence

  1. -c FILE selects a file; otherwise use MONIT_DOCKER_CONFFILE, then /etc/monit-docker/monit-docker.yml.

  2. If the selected path exists, load it. A read or parse error does not fall back to the environment.

  3. If it does not exist, use nonempty MONIT_DOCKER_CONFIG as inline YAML.

  4. Without either source, historical stats, monit, cron and serve use an empty configuration. check-config, run and scenario instead return 110.

The root must be a YAML mapping. {} is valid; an empty document, null or a list is not. The checked loader requires string keys and mapping-valued sections. It rejects unknown sections and entry fields. Every monitoring command validates all configured aliases and scenarios, including dormant entries, before Docker access. Configuration errors return 110. The file is rendered once; runtime and offline checks use the same validation. See the beta change notice before upgrading.

Section

Entry structure

Meaning

general

Mapping

Template context, not a mapping of CLI defaults

vars

Mapping

Shared template variables

clients

NAME: {config: MAPPING}

Docker SDK connection options; tls may be a boolean or mapping

ctn-groups

NAME: {match: [STRING, ...]}

Nonempty list of name:, id:, image: or label: patterns

dir-groups

NAME: {paths: [STRING, ...], access: MAPPING}

Nonempty absolute container paths; access is optional unless an access probe is requested

conditions

NAME: {expr: [STRING, ...]}

Nonempty list of conditions combined with AND

commands

NAME: {exec: [ACTION, ...]}

Nonempty ordered action sequence

stats

Mapping

rsc (ordered list), output (text or json)

monit

Mapping

rsc or cmd (ordered list), propagate_exit_code (boolean)

cron

Mapping

cmd, state_file, cooldown, trigger_after, max_gap, max_restarts, propagate_exit_code

serve

Mapping

Defaults for direct serve; explicit CLI options take precedence (see serve)

scenarios

NAME: MAPPING

A named stats, cron or serve job

These direct command sections are available since 1.3.0. Each command reads its own section. Explicit flags override individual YAML fields, even at their built-in default value. Repeating --rsc or --cmd / --cmd-if replaces the complete YAML list in argument order. For monit, choosing --rsc also discards configured commands and their exit-code policy for that invocation. Choosing --cmd discards configured resources. Use --no-propagate-exit-code to override propagate_exit_code: true. Simulation is requested with the --dry-run flag. Global selectors still go before the subcommand. Named scenarios keep their own settings.

Command/condition aliases and directory-group names use an ASCII letter followed by at most 64 letters, digits, underscores, dots or hyphens. Scenario names match [a-z0-9][a-z0-9-]{0,63} and are selected exactly. Client and container-group names have no equivalent restrictive naming pattern in the checked loader, but must be nonempty strings. Prefer simple printable names.

Entries may have local vars and @import_vars. Section imports use @import_client, @import_ctn-group, @import_dir-group, @import_condition, @import_command or @import_scenario; accept one path or a nonempty list. Imported files contain entries directly, without repeating the section wrapper. Relative imports resolve against the main file’s directory (the working directory for inline configuration). Later imports override earlier entries; main-file entries override imported entries as a whole, without deep merging. Imports are not a recursive include mechanism.

Mako templates run during rendering and must be trusted. They can access ENV, the my YAML helper and the supplied context. An imported file is rendered before its entry-local variables are available. Offline validation does not sandbox template execution. State/audit paths resolve against the working directory, not the configuration directory; absolute paths are preferable for scheduled jobs.

Directory access identities contain exactly uid, gid and groups: integer IDs from 0 through 4294967294, and 1..128 distinct supplementary group IDs including gid. Booleans are not IDs. Paths cannot contain ASCII control characters; duplicates are removed in order. Access probes require Python 3 in the target Linux container; see access checks for runtime restrictions.

Docker client selection

--client-from-env takes precedence over --client and configured clients. Otherwise, --client NAME selects an entry, or the first loaded client is used. With no configured clients the historical runtime uses Docker environment settings, defaulting DOCKER_HOST to unix:///var/run/docker.sock when absent. An explicitly named unknown client is always rejected, even if --client-from-env is also supplied. With a known name, --client-from-env still selects the environment.

Container selectors

Input

Current behavior

--name, --id, --image, --label

Repeatable; one pattern per value, surrounding whitespace removed; commas are never separators

Several patterns or kinds

OR: any name, ID, image tag or label value can select the container

-s STATUS

Repeatable status alternatives, applied in addition to the pattern selection

--ctn-group NAME

Repeatable exact group names; their patterns form a union, intersected with the union of direct name/ID/image/label patterns when supplied

No patterns/groups

All containers allowed by the status filter, including stopped containers

Ordinary pattern

Case-sensitive shell-style glob, matching the whole value

~EXPRESSION

Case-sensitive Python regular expression matched from the start; add $ for an end anchor or .* for a leading search

12-character alphanumeric ID pattern

Automatically gets a trailing * for legacy short-ID matching

Label pattern

Matches label values, not keys or Docker’s key=value filter syntax

Image pattern

Matches image tags, not image IDs/digests

Quote patterns to prevent shell expansion. Repeat options for several patterns: --name 'web-*' --name 'api-*'. YAML uses a list. Regex quantifiers such as --name '~worker-[0-9]{1,3}$' remain intact. Group matches and direct patterns use the same matching semantics; -s or select.status is an additional filter.

A pattern is at most 4096 characters. Regex syntax is checked with Python re; matching uses regex VERSION0 with a 50 ms limit per pattern/value comparison. Invalid, oversized or timed-out patterns fail explicitly, never selecting all containers as a fallback. This is not a total cycle deadline. Globs retain case-sensitive whole-value matching; regexes retain start-of-value matching.

An empty result is error 114, not a successful no-op. Container iteration order comes from Docker and is not promised to be sorted.

Global options

Options

Default / constraints

-c

Configuration precedence above

--client, --client-from-env

Client precedence above

--name, --id, --image, --label, --ctn-group, -s

Selection above; status is one of running, created, paused, restarting, removing, exited, dead

-l

info; critical, error, warning, info, debug

--logfile

MONIT_DOCKER_LOGFILE or /var/log/monit-docker/monit-docker.log

--runtimedir

MONIT_DOCKER_RUNTIMEDIR or /run/monit-docker

--event-window

300 seconds; integer 1..86400

--audit-file

MONIT_DOCKER_AUDIT_FILE; otherwise audit/events.jsonl beside the state file, or $XDG_STATE_HOME/monit-docker/audit/events.jsonl (~/.local/state fallback) when a journal is needed

--audit-max-bytes

5242880; integer at least 65536

--audit-files

5; integer 1..100

An empty audit path is invalid. Export/forwarding and authenticated journal reads require an explicit audit path (the environment variable also counts). Environment-derived configuration/log/runtime defaults are read when the CLI module is imported; set them before launching the process.

Rules and action aliases

--cmd and --cmd-if are aliases for the same repeatable option. A rule is an unconditional action, CONDITION ? ACTION, or @condition_alias ? @command_alias; inline and aliased sides can be mixed. A command alias expands to its ordered action list; a condition alias expands to conditions combined with AND. Alias entries do not recursively reference other aliases.

Docker actions are start, stop, remove, reload, restart, kill, pause, unpause. Parenthesized text such as (sh -c "echo ready") is a Docker exec command; use an explicit shell when shell syntax is needed. In a command alias, an action can be a string or a single-key mapping whose options are args (list) and kwargs (mapping), passed to the Docker SDK. Offline validation does not guarantee that every SDK keyword is supported. Completed exec status is required; detached or streaming execs cannot be reported as successful monitored actions.

Numeric comparisons use ==, !=, <, <=, >, >=, nonnegative numeric literals and, where supported, byte units such as MiB. State membership uses in (paused,running) or not in (paused, running). Spaces and tabs around commas are optional. Resource-specific bounds/operators are described in the check guides. Conditions for one directory group must be satisfied together by at least one path in that group. Separate groups and ordinary conditions are combined with AND.

Rules requiring only status/PID/health run before metric-dependent rules, even when their CLI order is interleaved. Within each phase, input order is preserved. Metric rules only run when the container is running or paused. This two-phase behavior remains when converting commands into scenarios. Chained numeric comparisons follow Python operand order: 10 < cpu_percent < 90 means 10 < cpu_percent and cpu_percent < 90. Bounds can include supported byte units.

Subcommands and outputs

Command

Options / behavior

stats

Repeatable --rsc; --output json (default) or text; no remediation

monit

Repeatable --rsc or repeatable --cmd/--cmd-if; --dry-run and --propagate-exit-code require rules

cron

Repeatable --cmd/--cmd-if and --state-file required; policy options below; --dry-run, --propagate-exit-code

serve

Continuous collection; repeatable --rsc and optional --cmd/--cmd-if; state required with rules; policy/listener options below

check-config

--output text (default) or json; optional repeatable --cmd/--cmd-if checked without running them

scenario list

JSON array of {name, mode, description}, sorted by name; no positional scenario name

scenario show NAME

One rendered scenario object as JSON; can contain command arguments; not a fully expanded default-option dump

run NAME

Run one exact scenario; optional --dry-run can enable but never disable simulation

maintenance

--state-file, exact lowercase 64-hex --container-id, --duration required; 0 resumes, 1..86400 seconds pauses automatic actions; JSON {container_id, maintenance_until}

restart-reset

--state-file and exact lowercase 64-hex --container-id required; JSON {container_id, status: "rearmed"}; requires recorded attempts

audit-export

--format jsonl (default) or csv; optional --category action|notification and --since with timezone

audit-send

Export filters plus required --url HTTPS receiver and optional --token-file; forwards JSON events and reports acknowledgements on stderr

audit-migrate (0.0.78+)

Simulation by default or --dry-run; --apply --output-dir NEW_DIRECTORY creates original-byte backups, converted journals and a verified manifest without connecting to Docker

stats --output json emits one JSON object per container, not a single array. Each maps the container name to requested resource values. Memory, disk and I/O values are human-readable unit strings where applicable. Text uses NAME|RESOURCE:VALUE|.... monit text keeps raw units. Unavailable displayed values may be null. Do not parse diagnostic logs as command output.

With no --rsc, stats and serve request mem_usage, mem_limit, mem_percent, cpu_percent, io_read, io_write, net_tx, net_rx, status, pid, health. Additional resources are oom_events, starts_recent, pids_current, pids_limit, pids_percent, and filesystem resources such as disk_percent[data]. See filesystem, access and runtime references for all resource meanings and prerequisites.

Rules emit JSON action decisions containing container_id, rule, command and status. Current statuses include executed, dry-run, cooldown, pending, restart-limit, maintenance. Nonmatching rules emit no decision. Docker exec output is not passed through as a structured stdout result. The first action failure aborts the remaining actions and containers.

check-config --output json returns {valid, errors, summary} on stdout. Errors contain location and message; validation stops at the first error. Summary counts clients, groups, commands, conditions, rules, plus scenarios when present. It validates dormant aliases/groups/scenarios as well as supplied rules. scenario list, show and run validate all configured scenarios and aliases. Their configuration errors go to stderr and return 110.

These inspection commands do not connect to Docker or create agent state/log files. maintenance and restart-reset also avoid Docker but mutate local state and write audit events. A dry run collects live Docker data; cron/serve can create a lock and audit simulated decisions while preserving durable policy state.

Policy and listener options

Option

Modes

Default / constraints

--state-file

cron, serve

Nonempty path; required in cron, or serve with rules/manual actions

--cooldown

cron, serve

300 seconds; finite and nonnegative

--max-restarts

cron, serve

3; positive integer, attempts per container until rearm

--trigger-after

cron, serve

0; finite and nonnegative; positive value requires rules and --max-gap

--max-gap

cron, serve

Finite positive seconds; only with positive trigger delay; must exceed interval in serve

--bind, --port

serve

IPv4 127.0.0.1, integer 9808 (1..65535)

--interval

serve

30 seconds; finite and at least 0.1

--stale-after

serve

max(90, 3 * interval); finite and at least interval

--dry-run

monit, cron, serve, run

Off; requires rules; incompatible with manual HTTP actions

--allow-actions

serve

Off; requires state, action token and exact HTTPS origin

--action-origin, --action-token-file

serve

Require --allow-actions; origin https://host[:port] without credentials/path/query/fragment; token file contains 64 lowercase hex characters with optional final newline

--action-cooldown

serve

30 seconds; finite and at least 1

--allow-maintenance

serve

Off; requires --allow-actions

--trust-proxy-user

serve

Off; requires manual actions or an audit read token. Trust the authenticated proxy to supply the username.

--audit-read-token-file

serve

Off; requires explicit audit path and a distinct read secret

--status-token-file

serve

Optional dedicated bearer secret for GET and HEAD on /v1/status

--metrics-token-file

serve

Optional dedicated bearer secret for GET and HEAD on /metrics

--notification-token-file

serve

Off; private notification ingestion secret

Keep one persistent state path per Docker host/job. A busy state lock fails immediately with 117. See cron, trigger delay, restart limits and serve for policy and token details.

Scenario fields and overrides

Scenario mode defaults to cron. All modes allow mode, description, select or all: true, client, client-from-env, event-window, audit-file, audit-max-bytes, audit-files. Selection fields are name, id, image, label, group, status, each a string or nonempty string list. Choose select or all: true, exclusively. Group selection intersects direct patterns; status is applied in addition.

Mode

Additional fields

stats

resources, output

cron

rules, state-file, cooldown, max-restarts, trigger-after, max-gap, dry-run

serve

All cron fields plus resources, bind, port, interval, stale-after

resources and rules accept a string or nonempty string list. Booleans must be YAML booleans, not strings. Integer fields accept integers or integer strings; other numeric fields accept finite numbers or numeric strings, including template results, then apply CLI bounds. Unknown or wrong-mode fields are rejected.

run/scenario reject global selectors and Docker-client overrides. Configuration, log and runtime paths are inherited; global audit/event-window settings apply unless the scenario defines them. CLI run --dry-run only enables simulation. The configuration is rendered once and passed to the runner. Scenario names do not identify cooldowns: changing the state path starts separate policy history. Manual HTTP action options and exec-exit propagation are not scenario fields.

Since 0.0.77, access resources in stats/serve scenarios require the directory group’s access identity during offline validation, just as access rules and execution do. Disk/inode/mount-mode resources do not require it.

Exit codes

Code

Meaning

0

Successful command; can mean no rule matched or all actions were skipped

2

Argument parsing/validation error, before command execution

110

Configuration, alias, scenario or value error handled as such

114

No container selected

115

Required observation unavailable

116

Action failure or unknown exec outcome. See exec wait limits

117

Persistent state lock busy

118

State read/write/validation failure

119

Audit error, or handled OS/value errors in local administration commands

140

Rule syntax error reaching the historical runtime CLI

150

Unexpected exception reaching the historical runtime CLI

170

Docker SDK connection/client error

180

Docker API error during collection; action API failures normally become 116

255

Interrupt/SystemExit handled inside the runtime CLI; not a promise for every signal or every entry point

Some monit --rsc invocations intentionally return data as the process status:

  • status: running 0, created 10, paused 20, restarting 30, removing 40, exited 50, dead 60.

  • health: healthy 0, starting 10, unhealthy 20, none 30, unknown 115.

  • One ordinary *_percent resource: integer truncated and bounded to 0..100. Raw stats/rule measurements are not capped. Filesystem percentages return per-path output instead.

These early exits describe the first selected container, not an aggregate. Use an exact container selector for a process-status check. --propagate-exit-code in monit/cron returns a failed completed exec’s 1..255 status instead of 116; it can overlap every other status family. Check command mode and diagnostics.

Decisions retained in 1.0

The five CLI/YAML choices were approved on 2026-09-27: shared strict validation, unknown-client rejection, group/direct intersection, literal selector commas, and Python-order chained comparisons. Their implementation is included in 0.0.80; see migration examples and limits.

Compatibility does not certify every deployment environment or workload. Local YAML remains the configuration model; the local CLI adds no login. Protected remote access retains its authentication.

Internal Python classes, diagnostic wording, implicit argparse abbreviations, Docker enumeration order and undocumented YAML behavior are not stable APIs.

Verification

test_cli_contract.py covers target selection, label values/image tags, regular expression boundaries, group/direct intersection, import replacement, client precedence and option placement. test_scenarios.py covers scenario overrides, offline inspection, single rendering, state continuity and access-resource validation. Existing test_check_config.py, test_monit_docker.py, test_cron.py, test_trigger_delay.py and test_restart_limit.py cover diagnostics, exit-code propagation and persistent policies. These tests support this inventory; they do not complete every milestone in the 1.0 roadmap.