← All writing

Personal · developer tools · Dockerfiles

I shipped DockRx: analyze Dockerfiles like a doctor, not a linter

Why I built a Dockerfile analyzer that explains impact and prioritizes fixes, how Cursor accelerated the build, and what v0.1.x intentionally leaves out.

TL;DR: DockRx analyzes Dockerfiles and explains size, caching, and security problems with actionable fixes — not just lint warnings. You get a health score, quick wins, estimated impact, and (for a subset of rules) non-destructive auto-fixes. Install with pip install dockrx or uv tool install dockrx. Try the playground with no install at dockrx.vercel.app. v0.1.1 is on PyPI and GitHub Releases.

This post covers the problem, the product, how we built it with AI without shipping slop, and what the first release does not do yet.

The loop I was tired of

Most Dockerfile “optimization” still looks like this:

  1. Paste a Dockerfile into a linter
  2. Get a wall of rule IDs and style nits
  3. Guess which ones actually matter for image size / cache / security
  4. Rewrite by hand, hope you didn’t break something
  5. Rebuild and wait
  6. Repeat

Tools like Hadolint are excellent at linting Dockerfiles — especially shell hygiene inside RUN. They still tend to stop at warnings, not treatment. You leave knowing something is wrong, not what to do first or what you’ll get back.

I wanted something closer to a diagnosis:

Analyze → score the patient → show the highest-ROI fixes → preview treatment → compare before/after.

That became DockRx.

What DockRx does

At a high level:

parse Dockerfile → instruction graph
        ↓
run hybrid rules (YAML + Python plugins)
        ↓
score findings (overall + categories)
        ↓
render diagnosis (health, quick wins, lost points, estimates)
        ↓
optional: explain / fix / suggest / compare / badge / format

Install

Requires Python 3.12+.

uv tool install dockrx
# or: pipx install dockrx
# or: pip install dockrx

From GitHub (repo name is docrx; the CLI/package is dockrx):

git clone https://github.com/kiwi-07/docrx.git
cd docrx
uv sync
uv run dockrx --version

Basic usage

dockrx analyze .
dockrx analyze Dockerfile --json
dockrx analyze . --score-only

dockrx explain DRX018
dockrx fix . --dry-run --yes
dockrx compare before/ after/
dockrx badge . --format markdown
dockrx suggest . --yes
dockrx format Dockerfile --check

Useful commands:

Command Meaning
analyze Full health report (or --json / --score-only / --badge)
explain Deep dive on a rule ID
fix Non-destructive rewrite to Dockerfile.fixed (subset of rules)
suggest Walk fixes interactively
compare Before/after score and finding deltas
badge SVG / markdown / shields-style badge
format Style formatter (--check / --diff / --write)

Try it without installing

Paste a Dockerfile into the live playground:

https://dockrx.vercel.app

The web UI is intentionally analyze-only. The CLI still owns fix / compare / badge / format and project-context checks (like whether a .dockerignore exists).

Try the bundled example

git clone https://github.com/kiwi-07/docrx.git
cd docrx
uv sync
uv run dockrx analyze examples/test

You should see something like:

Overall Health
█████████░░░░░░░░░░░
47 / 100
Needs Attention · Grade: F (Poor)

Quick Wins
1. Switch JDK → JRE runtime          ↓ ~100–300 MB   2 min
2. Add .dockerignore                 ↑ 20-70%        30 sec
3. Remove debug networking tools     ↓ ~15–80 MB     1 min

There’s a terminal capture of this flow in the README.

From 47 to 85 with one command

Analysis is only half the point. Here’s the same example after dockrx fix --yes, which rewrites a subset of rules into Dockerfile.fixed (your original is never touched):

 # prepare runtime env
-FROM eclipse-temurin:21-jdk-jammy
+FROM eclipse-temurin:21-jre-jammy
 LABEL maintainer="Example Team <maintainer@example.com>"

 RUN apt-get update && apt-get install -y --no-install-recommends \
-    iputils-ping \
-    telnet \
     curl \
     wget \
-    traceroute \
-    netcat \
-    net-tools \
     iproute2 \
-    dnsutils \
  && rm -rf /var/lib/apt/lists/*

-ENTRYPOINT exec java $JAVA_OPTS -jar /opt/app/service-events/service-events.jar
+RUN useradd --system --create-home appuser && chown -R appuser:appuser /opt/app
+USER appuser
+RUN printf '%s\n' '#!/bin/sh' 'exec java $JAVA_OPTS -jar /opt/app/service-events/service-events.jar' > /entrypoint.sh && chmod +x /entrypoint.sh
+ENTRYPOINT ["/entrypoint.sh"]

Plus a generated .dockerignore. Re-analyze the result and the score moves the way you’d hope:

47 / 100  →  85 / 100     (applied: DRX018, DRX005, DRX019, DRX003, DRX023)

JDK → JRE, debug tooling out of the runtime image, a non-root user, an exec-form entrypoint, and a .dockerignore — the five highest-ROI fixes, previewed before anything is written.

What it finds on real projects

To keep myself honest, I ran DockRx against 18 Dockerfiles from popular open-source projects (Grafana, Keycloak, Gitea, Prometheus, Traefik, MinIO, and friends). The spread:

Metric Value
Best filebrowser — 99
Worst keycloak — 47
Median 73
Average 72
Scoring below 70 6 of 18

The most common findings weren’t exotic: missing non-root USER, :latest/untagged bases, missing HEALTHCHECK, and debug tooling shipped into runtime images — the boring stuff linters mention but rarely prioritize. The full regenerated scoreboard lives in examples/realworld/RESULTS.md.

(Scores use DockRx’s “spill” model and are deliberately harsher than a naive lint count.)

Design choices that matter

1. Diagnosis over lint spam

A finding without impact ranking is noise. DockRx leads with overall health, grade, quick wins, lost points, and estimated results — then the detailed ROI-ranked list. The goal is “what should I do in the next 10 minutes?”, not “here are 40 rule IDs.”

2. Hybrid rule engine

Simple patterns live as YAML. Messier logic (stage-aware checks, package-manager cache order, JDK-in-runtime heuristics) lives as Python plugins. Teams can override either via .dockrx/rules and .dockrx/plugins.

3. Fixes are non-destructive by default

dockrx fix writes Dockerfile.fixed. It never overwrites your original unless you opt into something like format --write. That made it safe to ship auto-fixes early without pretending every rule is auto-fixable.

4. Estimates are labeled as heuristic

Size and build-time numbers are not measured from real builds in Milestone 1. The report says so. Shipping fake precision would be worse than shipping honest heuristics.

5. Exit codes are a public contract

--json, --score-only, and --badge are mutually exclusive. Empty/invalid Dockerfiles exit 2. Unknown explain IDs exit 1. Non-TTY fix/suggest refuse to prompt. CI should be able to trust the CLI without reading the human report.

6. Scoring must move when you fix something

An early scoring bug floored category scores at zero and hid further progress. The spill model counts every finding toward the overall score, so fixing anything always moves the number. Category displays stay floored at 0.

7. Hadolint is a partner, not a rival

DockRx is not trying to replace Hadolint’s shell linting. The pairing that actually works:

  • Hadolint for deep Dockerfile + shell linting
  • DockRx for prioritization, explainability, fixes, and team-facing UX

DockRx is also not a CVE scanner.

MVP scope (honest)

Supported in v0.1.x:

  • Static Dockerfile parsing into an instruction graph
  • Built-in YAML rules + Python plugins (~27 rule IDs)
  • Rich terminal diagnosis + JSON for CI
  • analyze / explain / fix / suggest / compare / badge / format
  • Project-local rule/plugin overrides
  • GitHub Action with PR commenting + severity gate
  • Live playground + small public HTTP API

Not yet (intentional cuts, not forever):

  • Measured image/layer sizes from real builds
  • Full auto-fix coverage for every rule
  • Remote rule packs
  • VS Code extension
  • HTML reports as a first-class product surface

If a Dockerfile is empty or missing FROM, we fail loudly — never invent a “clean” report.

If you want to contribute, the highest-value areas are richer auto-fixes, measured metrics, and more language/ecosystem cache rules. File issues or PRs against develop on github.com/kiwi-07/docrx.

How AI helped build this (without becoming the product)

I built DockRx with Cursor as a pair programmer — not as an autopilot that one-prompted a repo onto PyPI.

What AI was good at

  • Scaffolding the Typer CLI surface once the command set was decided
  • Rule/plugin boilerplate and presentation metadata
  • Test suites once success criteria were explicit (exit codes, scoring spill, API validation)
  • Release choreography: README/docs accuracy, CHANGELOG, Vercel playground, packaging, tags

What AI was not allowed to own alone

  • Product thesis — “doctor, not linter” came from the pain of warning-only tools
  • Scope cuts — analyze-only web UI; CLI keeps the sharp tools
  • Failure semantics — exit codes, invalid Dockerfile handling, non-TTY refusal
  • QA — treat the agent like a junior: prescribe smoke tests, re-run, demand fixes, re-verify against real-world Dockerfiles

The workflow that actually worked

  1. Write a short product brief (problem, non-goals, CLI UX)
  2. Let the agent implement against that brief
  3. Run a prescribed QA matrix myself (CLI + UI + real-world examples)
  4. Feed failures back as concrete bugs
  5. Re-run the same matrix until green
  6. Only then: docs, playground link, PyPI, branch protection

AI compressed calendar time. It did not remove engineering judgment. The quality bar was: would I trust this on a laptop while reviewing a teammate’s Dockerfile?

If you’re shipping with agents, the underrated skill is writing acceptance tests and invariants the agent can fail against. Vague “make Docker better” prompts produce demos. Explicit “exit 2 on invalid FROM / never overwrite without --write / fixing must move overall score” prompts produce tools.

Architecture sketch

Rough module map under src/dockrx/:

Module Role
cli.py Typer entrypoint
parser/ Dockerfile → instruction graph
engine/ Rule loading + runner
rules/builtin/ YAML rule definitions
plugins/ Python checks
scoring/ Severity → health score
reporters/ Rich / JSON / compare / explain / badge
fixer.py Deterministic non-destructive fixes
formatter.py Dockerfile style formatter

Plus a small FastAPI surface in api/ for the playground.

Repo defaults:

  • Day-to-day work on develop
  • Releases via PR into main, then tag (v0.1.0, v0.1.1, …)
  • Branch protection on main (PR + review)

Release checklist we used for v0.1.x

  • Fix CLI/API QA gaps and regenerate real-world scoreboard
  • Document exit codes and the public web API
  • Sanitize demo fixtures and scrub private paths from git history
  • Merge developmain
  • Tag v0.1.0, publish to PyPI, verify install
  • Patch v0.1.1 for corrected author metadata
  • Verify: uvx --from dockrx dockrx --version

Links:

Who should try it today

Good fit if you:

  • Care about image size, cache order, and runtime hardening — not only style lint
  • Want prioritized fixes you can explain to a teammate
  • Already use Hadolint (or want something that pairs with it)
  • Prefer a CLI-first tool with an optional playground

Wait (or open an issue) if you need measured layer sizes from real builds, full auto-fix coverage, or a VS Code extension on day one.

Closing

DockRx is small on purpose. The bet is that prioritized diagnosis is more valuable than pretending to be a full Docker platform on the first release.

If you try it on a real Dockerfile and it breaks in an interesting way, please file an issue — that feedback is how the roadmap gets ordered.

uv tool install dockrx
dockrx analyze .
# or just paste into https://dockrx.vercel.app

Thanks for reading — and if you build with AI too: keep the taste and the tests human.

← More writing Home