Features
Everything you need to lint, diagnose, and fix Docker configurations — from static file checks to live container analysis.
50 Checks
25 static checks for Dockerfiles, compose files, secrets, line endings, and .dockerignore. 25 runtime checks for builds, startup, networking, performance, images, and cleanup.
Auto-Fix
Safe fixes applied automatically with --fix or via interactive prompts. Manual fixes include paste-ready code examples.
Interactive TUI
Split-pane terminal UI to browse results by severity, view fix details with YAML syntax highlighting, and copy code to clipboard.
Clipboard Copy
Press c in the TUI to extract and copy only the code blocks from fix instructions — properly dedented, ready to paste.
Smart Discovery
Auto-detects compose files by standard names or content sniffing. Non-standard filenames like infra.yml are found automatically.
CI-Friendly
JSON output, deterministic exit codes, zero interactive prompts. Drop --ci into your GitHub Actions workflow.
Quick Start
Install
npm install -g dockerdoctor
Or use without installing:
npx dockerdoctor
Run
# Launch interactive mode (default)
dockerdoctor
# Lint just the Dockerfile
dockerdoctor dockerfile
# Check a specific compose file
dockerdoctor compose -c docker-compose.prod.yml
# JSON output for CI
dockerdoctor --json --severity error
# Auto-apply all safe fixes
dockerdoctor --fix
Fix
Browse results in the TUI, read fix instructions with YAML highlighting, press c to copy code, and paste directly into your files.
Interactive Mode
Running dockerdoctor with no flags launches interactive mode — a guided wizard that walks you through scanning.
Scan Target
- Scan current directory — check CWD for Docker files
- Scan subdirectories — recursively find Docker projects up to 4 levels deep
- Specify a directory path — enter any custom path
- Docker daemon only — skip file scanning, check running containers and images
Check Selection
Pick All checks or select specific categories: Dockerfile, Compose, Secrets, Line Endings, Dockerignore, Build, Startup, Network, Performance, Image, or Cleanup.
Subdirectory scanning automatically skips node_modules, .git, dist, build, .next, __pycache__, and vendor.
TUI Browser
When results are found, the TUI shows a split-pane view with severity categories on the left and full detail on the right.
────────────────────┬────────────────────────────────────
▸ x Errors (3) │ Errors
! Warnings (5) │ 3 issues
i Info (2) │
│ x Service "web" has no healthcheck
│ x Undefined network "backend"
│ x Hardcoded secret in ENV
────────────────────┴────────────────────────────────────
↑↓ Navigate Enter: Open q: Exit
Keyboard Shortcuts
| ↑ / ↓ or j / k | Navigate results |
| Enter or → | Open category / view detail |
| ← or q | Go back |
| Shift+↑ / Shift+↓ | Scroll detail pane |
| c | Copy fix code to clipboard |
| Ctrl+C | Exit |
The c key extracts only code blocks from fix instructions — dedented and ready to paste directly into your Dockerfile or compose file.
Commands & Flags
Subcommands
dockerdoctor
Launch interactive mode (default)
dockerdoctor check
Run all checks (default with --json/--ci/--fix)
dockerdoctor dockerfile
Lint Dockerfile only — 9 checks
dockerdoctor compose
Lint compose file only — 7 checks
dockerdoctor secrets
Scan for hardcoded secrets — 4 checks
dockerdoctor lineendings
Check for CRLF issues — 3 checks
dockerdoctor build
Diagnose build failures — 4 checks (Docker)
dockerdoctor startup
Diagnose startup failures — 4 checks (Docker)
dockerdoctor network
Diagnose networking issues — 4 checks (Docker)
dockerdoctor perf
Diagnose performance issues — 4 checks (Docker)
dockerdoctor image
Analyze image for bloat — 4 checks (Docker)
dockerdoctor cleanup
Find reclaimable disk space — 5 checks (Docker)
Flags
| Flag | Description | |
|---|---|---|
--json | Output results as JSON | |
--ci | CI mode (JSON output + exit codes) | |
--fix | Auto-apply all safe fixes | |
--severity <level> | Minimum severity: error, warning, or info | |
-f, --file <path> | Path to Dockerfile | |
-c, --composefile <path> | Path to compose file | |
-V, --version | Print version | |
-h, --help | Print help |
Static Checks No Docker Required
25 checks that analyze your files without needing a running Docker daemon.
Dockerfile — 9 checks
| Check | What it catches | |
|---|---|---|
Base image :latest | Non-deterministic builds from unpinned base image tags | |
| Running as root | Container processes running as root user | |
| Shell form CMD | Shell form instead of exec form for CMD/ENTRYPOINT | |
| Layer ordering | COPY . . before package file copy invalidates cache | |
| npm install | Using npm install instead of npm ci | |
| Missing multi-stage | Single-stage builds that include build tools in the final image | |
| NODE_ENV trap | NODE_ENV set before npm ci blocks devDependencies | |
| Missing CHOWN | COPY/ADD without --chown leaves files owned by root | |
| Alpine cache | Missing --no-cache or rm -rf /var/cache/apk/* |
Compose — 7 checks
| Check | What it catches | |
|---|---|---|
| Missing healthcheck | Services with ports but no healthcheck block | |
| Undefined network | Service references a network not in the top-level networks block | |
| Network mismatch | Services that need to communicate are on different networks | |
| Static IP | Hardcoded IP addresses in service network configuration | |
| Bind mounts | Host-path bind mounts that break portability | |
| Bridge network | Using the default bridge network instead of custom networks | |
| Swarm ignored | Swarm-specific config (deploy) ignored in docker compose |
Secrets — 4 checks
| Check | What it catches | |
|---|---|---|
| Dockerfile ENV | Hardcoded passwords/tokens in ENV instructions | |
| Dockerfile ARG | Secrets passed via ARG (visible in image history) | |
| Compose environment | Hardcoded secrets in compose service environment | |
| Sensitive COPY | Copying .env, key files, or credentials into the image |
Line Endings — 3 checks
| Check | What it catches | |
|---|---|---|
| CRLF detection | Windows line endings that break shell scripts in Linux containers | |
| Missing .gitattributes | No .gitattributes to enforce consistent line endings | |
| Missing dos2unix | No dos2unix conversion step in the Dockerfile |
Dockerignore — 2 checks
| Check | What it catches | |
|---|---|---|
| Missing file | No .dockerignore file, causing bloated build context | |
| Missing entries | Common entries like node_modules, .git not excluded |
Runtime Checks Requires Docker
25 checks that inspect your running Docker daemon, containers, and images.
Build — 4 checks
| Check | What it catches | |
|---|---|---|
| Large build context | Build context too large, slowing down builds | |
| DNS resolution | DNS failures during build (can't download packages) | |
| Disk space | Insufficient disk space to complete a build | |
| Platform mismatch | Building for wrong platform (e.g., ARM on x86) |
Startup — 4 checks
| Check | What it catches | |
|---|---|---|
| Exit code analysis | Decodes exit codes (137=OOM, 126=permission, 127=not found) | |
| OOM killed | Containers killed by the OOM killer | |
| Environment variables | Missing or empty required environment variables | |
| Entrypoint exists | CMD/ENTRYPOINT binary not found or not executable |
Network — 4 checks
| Check | What it catches | |
|---|---|---|
| Same network | Containers that should communicate are on different networks | |
| DNS resolution | DNS resolution failures between containers | |
| Port conflicts | Multiple containers trying to bind the same host port | |
| Localhost binding | Services bound to 127.0.0.1, unreachable from other containers |
Performance — 4 checks
| Check | What it catches | |
|---|---|---|
| Resource usage | High CPU or memory consumption | |
| Bind mount I/O | Slow I/O from host-mounted volumes | |
| Build cache | Docker build cache not being utilized effectively | |
| Resource limits | No memory/CPU limits set, risking resource exhaustion |
Image — 4 checks
| Check | What it catches | |
|---|---|---|
| Image size | Oversized images that waste bandwidth and storage | |
| Layer analysis | Inefficient layer structure, duplicate content | |
| Architecture mismatch | Running ARM images on x86 or vice versa (via emulation) | |
| Base image bloat | Base image includes unnecessary tools and libraries |
Cleanup — 5 checks
| Check | What it catches | |
|---|---|---|
| Disk usage | Docker consuming excessive disk space | |
| Dangling images | Untagged images left behind by builds | |
| Stopped containers | Exited containers wasting disk space | |
| Unused volumes | Volumes no longer attached to any container | |
| Build cache | Build cache consuming significant disk space |
Compose Discovery
dockerdoctor automatically finds compose files using a two-pass strategy:
Standard names
Checks for docker-compose.yml, docker-compose.yaml, compose.yml, and compose.yaml.
Content sniffing
If no standard file is found, scans all .yml / .yaml files for a services: key. This catches non-standard filenames like infra.yml or stack.yaml.
Automatically skips CI configs (.gitlab-ci.yml, azure-pipelines.yml), Kubernetes manifests (Chart.yaml, values.yaml), and build tool files (mkdocs.yml, pnpm-lock.yaml).
CI/CD Integration
GitHub Actions
- name: Lint Dockerfiles
run: npx dockerdoctor --ci --severity error
The --ci flag outputs JSON and uses exit codes:
- Exit 0 — no errors found
- Exit 1 — errors found (warnings and info alone don't trigger failure)
- Exit 2 — invalid arguments (bad file path, invalid severity)
Programmatic API
Import and run checks from your own scripts:
import { buildContext, runChecks } from 'dockerdoctor';
const context = await buildContext(process.cwd());
const report = await runChecks(context, {
categories: ['dockerfile', 'secrets'],
minSeverity: 'warning',
});
console.log(`Found ${report.summary.errors} errors`);
for (const result of report.results) {
console.log(`[${result.severity}] ${result.title}: ${result.message}`);
}
Exports
| Function | Description | |
|---|---|---|
buildContext(cwd) | Auto-detect and parse Docker files in a directory | |
runChecks(context, opts) | Execute checks and return a Report | |
registerCheck(check) | Register a custom check | |
getAllChecks() | Get all registered checks | |
getChecksByCategory(cat) | Get checks for a specific category | |
parseDockerfile(raw, path) | Parse a Dockerfile string into structured data | |
parseCompose(raw, path) | Parse a compose file string | |
findComposeFile(dir) | Find a compose file using standard names + sniffing |