chainlaunch doctor runs an ordered battery of checks against the local
ChainLaunch instance and the nodes it manages, prints a report, and exits
non-zero when something is wrong. Run it before opening a support ticket.
chainlaunch doctorFlags
| Flag | Description | Default |
|---|---|---|
-o, --output |
auto, text, or json. |
auto (text when stdout is a TTY, otherwise json) |
--category |
Limit to one or more categories: host, process, db, network, blockchain, security, observability, backup. Repeatable or comma-separated. |
all |
--timeout |
Per-check timeout. | 5s |
--budget |
Overall wall-clock budget. | 30s |
--severity |
Minimum severity to print: pass, warn, fail. Failures always affect the exit code regardless of this setting. |
warn |
--no-color |
Disable ANSI colours. Implied when stdout is not a TTY. | unset |
Exit codes
| Code | Meaning |
|---|---|
0 |
All checks passed. No failures, no warnings. |
1 |
Warnings present, no failures. |
2 |
One or more failures. |
3 |
Doctor itself errored, for example it could not read the config. |
Because the exit code is deterministic, chainlaunch doctor works directly as
a CI gate or a health probe.
Output
Checks complete out of order, but output is sorted by category then name, so two runs diff cleanly. The JSON output mirrors that ordering.
chainlaunch doctor — v0.42.1 (a3f8b2c) — host: ip-10-0-1-23 — 2026-05-13T08:42:11Z
HOST 4/4 pass, 1 warn
✓ host.os_supported linux/amd64
✓ host.cpu_count 8 cores
! host.memory_total 3.4 GB available, ≥ 4 GB recommended
✓ host.disk_free 71% free on /var/lib/chainlaunch
✓ host.docker_running server 25.0.3, overlay2
BLOCKCHAIN 2/3 pass, 1 fail
✓ blockchain.orderer_quorum (network: prod-supply-chain) 3 orderers, tolerates 1
✗ blockchain.cert_expiry (network: prod-supply-chain) peer0.org2 TLS expires in 6 days
→ Renew with: POST /api/v1/nodes/{id}/certificates/renew
───────────────────────────────────────────────────────────────────────
STATUS: AT_RISK · 28 pass · 2 warn · 1 fail · 0 timeout · 2.4s
───────────────────────────────────────────────────────────────────────
Use --output json for machine-readable output.
Check inventory
Each check declares a category and a default severity. The runner orders them by category, then by name, and applies the configured timeout to each.
host
| Check | What it asserts | Severity |
|---|---|---|
host.os_supported |
OS is in the supported matrix (linux/amd64, linux/arm64, darwin/arm64, darwin/amd64). | warn |
host.kernel_version |
Kernel ≥ 5.x on Linux. | warn |
host.glibc_version |
glibc ≥ 2.31 on Linux. Only meaningful in service mode. | warn |
host.cpu_count |
≥ 2 cores, with 4 recommended. | warn |
host.memory_total |
≥ 4 GB RAM. Warns under 4 GB, fails under 2 GB. | warn / fail |
host.disk_free |
Fails under 10% free, warns under 20%. | warn / fail |
host.swap_active |
Swap is configured. Warns if absent on a low-RAM host. | warn |
host.time_skew |
Clock is within 1s of a stable monotonic reference. | warn |
host.fd_limit |
ulimit -n ≥ 65535. |
warn |
host.iptables_present |
iptables exists when running nodes in docker mode. |
warn |
host.docker_running |
docker info succeeds if any node runs in docker mode. |
fail |
host.docker_storage_driver |
Storage driver is overlay2 or btrfs. |
warn |
host.docker_disk_usage |
Docker's data-root partition has headroom. | warn |
host.data_dir_perms |
The data directory is owned by the running user, mode ≤ 0750. | warn |
host.binary_path |
The chainlaunch binary on PATH matches the running version. Catches stale binaries. |
warn |
process
| Check | What it asserts |
|---|---|
process.server_alive |
chainlaunch serve is responding on its configured port. |
process.server_version_matches_cli |
The API reports the same git commit as the CLI. |
process.server_uptime |
Server has been up ≥ 60s. Catches crash loops. |
process.api_latency_p50 |
A trivial /health request returns in under 250 ms. |
process.outbound_dns |
The server can resolve registry-1.docker.io. Warn-level for air-gapped installs. |
process.config_loadable |
The effective config parses. |
process.pending_migrations |
No database migrations are pending. |
db
| Check | What it asserts |
|---|---|
db.reachable |
The SQLite file is readable and PRAGMA integrity_check returns ok. |
db.foreign_keys_enabled |
PRAGMA foreign_keys is 1. |
db.wal_size |
WAL file is under 1 GB. A larger file signals a stuck checkpoint. |
db.last_vacuum |
VACUUM ran within 30 days. Informational. |
db.row_counts |
Row counts for the 10 largest tables. Surfaces runaway growth in audit or metrics tables. |
network
| Check | What it asserts |
|---|---|
network.node_reachable |
TCP connect succeeds to each node's listen address. |
network.node_tls_handshake |
TLS handshake completes and the chain validates against the configured CA. |
network.node_grpc_healthy |
Fabric peers return SERVING on the gRPC health probe. |
network.node_rpc_healthy |
Besu nodes answer eth_blockNumber. |
network.peer_endpoint_overrides_consistent |
Configured address overrides resolve. |
network.docker_publish_collisions |
No other process holds a node's published port. |
blockchain
Covers orderer quorum, validator count, TLS, certificate expiry, node health, endpoint conflicts, backup schedule, and org diversity, plus:
| Check | What it asserts |
|---|---|
blockchain.chaincode_definition_drift |
The local chaincode definition for each Fabric channel matches what is committed on-channel. |
blockchain.besu_block_height_drift |
All Besu nodes in a network are within 5 blocks of each other. |
blockchain.fabric_channel_height_drift |
All peers joined to a channel are within 2 blocks of each other. |
security
| Check | What it asserts |
|---|---|
security.tls_listener_enabled |
The ChainLaunch API is served over TLS. |
security.default_admin_changed |
The admin account no longer uses the install default password. |
security.api_key_count |
At least one non-default API key exists when an external integration is configured. |
security.key_provider_health |
Each configured key provider (database, AWS KMS, Vault) answers a no-op probe. |
security.kms_credentials_present |
The AWS KMS credential chain resolves. |
security.vault_token_alive |
The Vault token can look itself up and has ≥ 7 days TTL remaining. |
security.encryption_key_set |
An encryption-at-rest master key is configured. |
security.oidc_refresher_healthy |
The OIDC refresher ran within the last 30 minutes. |
observability
| Check | What it asserts |
|---|---|
observability.metrics_port_listening |
The Prometheus endpoint is up. |
observability.notifications_configured |
At least one notification channel is configured. |
observability.audit_pruner_recent |
The audit pruner ran within the retention window. |
backup
| Check | What it asserts |
|---|---|
backup.target_count |
At least one active backup target exists. |
backup.schedule_count |
At least one active backup schedule exists. |
backup.last_successful_within_sla |
Each target's most recent backup succeeded within twice its schedule interval. |
backup.target_verify |
Verifies the latest backup for each target. This is the slowest check. |
backup.s3_reachable |
S3 targets can be listed with the configured credentials. |
backup.ebs_role_present |
The IAM role for EBS targets resolves. |
Common usage
Gate a deploy on a clean bill of health:
chainlaunch doctor --severity fail || exit 1Check only what you changed:
chainlaunch doctor --category backup,securityCapture a machine-readable report:
chainlaunch doctor --output json > doctor-report.jsonGive slow checks more room:
chainlaunch doctor --category backup --timeout 30s --budget 120s