Developer toolkit for production teams containerizing Spring Boot — with optional benchmark evidence for tuning and conference demos.
springdocker is a Python CLI that helps teams inspect a Spring Boot project, commit Dockerfile strategy in .springdocker.toml, generate and verify Dockerfiles in CI, and (optionally) run benchmark suites for evidence-backed tuning.
See docs/POSITIONING.md for who it is for, CI-evidenced guarantees, and how the sample projects relate to shipped behavior.
Resolved in #87 — see docs/adr/0008-target-audience.md.
| Audience | Fit |
|---|---|
| Production teams (primary) | Adopt via PyPI on your Maven/Gradle service — config-first Dockerfile workflow, explain/verify in CI, Java 17+ on your project |
| Conference / storytelling (secondary) | Clone for presentations and benchmark evidence under samples/java-spring-docker/ — numbers are sample-specific, not universal guarantees |
| Personal lab only (not primary) | Bleeding-edge sample (Boot 4 / Java 25) stress-tests the generator inside the repo; you do not need to match those versions to use the CLI |
Not a black-box image builder like Jib or Buildpacks — you own the Dockerfile. Not a research-only toy — CI gates the installable CLI; benchmarks and decks are optional depth.
Primary path: PyPI — install the CLI and run it on your Spring Boot project. You do not need to clone this repository for Dockerfile generation, explain, or verify.
# Recommended: isolated user install (Dockerfile workflow)
pipx install springdocker
# or
uv tool install springdocker
# Include benchmark run/analyze (optional; requires Docker on the host)
pipx install 'springdocker[benchmark]'
# or: python3 -m pip install 'springdocker[benchmark]' inside your project venvSee cli/README.md for pip/editable options and upgrade commands.
| Goal | What to do |
|---|---|
| Generate/explain/verify Dockerfiles for your service | Install from PyPI only |
| Reproduce benchmark evidence, presentations, or pinned CI baselines | Clone; work under samples/java-spring-docker/ — see docs/presentation/README.md for deck ownership and update policy |
| Contribute to the CLI | Clone; editable install — see Contributing |
Resolved in #97 — see docs/adr/0006-pypi-first-distribution.md.
springdocker is the canonical name for this project — use it when searching GitHub or PyPI, installing the package, or running the CLI.
| Surface | Name |
|---|---|
| GitHub repository | mnafshin/springdocker |
PyPI package / pip install |
springdocker |
| CLI command | springdocker |
| Config file | .springdocker.toml |
The string java-spring-docker appears only in the benchmark sample app, not in the CLI package:
| Surface | Path or coordinates | Role |
|---|---|---|
| Sample app directory | samples/java-spring-docker/ |
Full Spring Boot app for benchmark scenarios and evidence |
| Sample Maven/Gradle artifact | io.github.mnafshin:java-spring-docker |
Demo application identity inside that sample |
Those sample names predate the springdocker product name. They do not affect installation (pip install springdocker) or CLI usage. Your local clone directory can be named anything.
- Jib and Buildpacks optimize for build convenience and opaque image assembly.
- springdocker optimizes for teams that want a real Dockerfile they can own, read, and edit.
- It combines explicit Dockerfile generation with explainability and verification workflows.
explainis advisory static analysis for human review;verifyis the pass/fail command for CI gates.
See docs/POSITIONING.md for the detailed comparison and tradeoffs.
flowchart LR
dev[Developer] --> cli[springdocker CLI]
cli --> cfg[.springdocker.toml]
cli --> proj[Spring Boot project]
cli --> df[Generated Dockerfile]
cli --> bench[Benchmark variants + raw CSV]
bench --> report[Table / JSON analysis]
See docs/architecture.md for the detailed module map and command lifecycle.
The repo is split into these main surfaces:
src/springdocker/- installable CLI package and core implementation.cli/README.md- command reference and configuration details.
See Sample project map for which Spring Boot path to use.
Shipped and CI-validated: project detection, config, Dockerfile generation, explain/verify commands, and benchmark asset/analyzer plumbing (see docs/POSITIONING.md).
Optional / sample-anchored: full benchmark runs, performance comparison tables, and reference evidence under samples/java-spring-docker/.
- Detects Maven or Gradle projects.
- Writes a starter
.springdocker.tomlconfig. - Generates Dockerfiles with opinionated Spring Boot defaults (
jvm-balanced: distroless runtime + custom jlink runtime + layered JAR). - Pins generated base images by digest when known.
- Creates benchmark variants and runs benchmark suites (requires Docker and
[benchmark]extra). - Summarizes benchmark CSV output as a table or JSON.
Digest pins are centralized in src/springdocker/digest_pins.py and verified in CI.
Runbook: docs/digest-pin-runbook.md · Renovate template: .github/renovate.json
| Path | Role | Use when |
|---|---|---|
tests/fixtures/{maven-only,gradle-only}/ |
Minimal Spring Boot apps for CLI walkthroughs and CI | Learning the CLI, trying Dockerfile generation, or extending tests (docs/golden-samples.md) |
samples/java-spring-docker/ |
Benchmark harness + evidence | Running benchmark scenarios and comparing raw.csv results |
Gradle walkthroughs use tests/fixtures/gradle-only/ with the same commands below (Maven: tests/fixtures/maven-only/).
See docs/adr/0004-sample-project-strategy.md for why the former examples/ walkthrough tree was removed.
Install from PyPI first (see Install). Then run against your Spring Boot project:
cd /path/to/your-spring-boot-app
springdocker doctor --project-root .
springdocker init --project-root . --build-tool maven
springdocker configure --project-root . --force
springdocker dockerfile generate --project-root .
springdocker explain --project-root . Dockerfile.generated --config-aware # advisory review
springdocker verify --project-root . Dockerfile.generated --check-config-drift # CI gateTo try the CLI without your own app, clone this repo and use the minimal fixtures (see Sample project map):
git clone https://github.com/mnafshin/springdocker.git
cd springdocker
pipx install 'springdocker[benchmark]' # or: pip install -e '.[dev]' for contributing
springdocker doctor --project-root tests/fixtures/maven-only
springdocker init --project-root tests/fixtures/maven-only --build-tool maven
springdocker configure --project-root tests/fixtures/maven-only --force
springdocker dockerfile generate --project-root tests/fixtures/maven-onlyBenchmark workflow (optional; requires clone + Docker + [benchmark] extra) — use the full sample app under samples/:
cd springdocker # repository root after clone
springdocker benchmark generate --project-root samples/java-spring-docker --java-version 25
springdocker benchmark run --project-root samples/java-spring-docker --profile quick
springdocker benchmark analyze --project-root samples/java-spring-docker samples/java-spring-docker/benchmarks/03-custom-jre-jlink/results/raw.csv --format table
springdocker benchmark compare --project-root samples/java-spring-docker samples/java-spring-docker/benchmarks/03-custom-jre-jlink/results/raw.csv --baseline-variant with-jlink-runtime
springdocker benchmark analyze --project-root samples/java-spring-docker samples/java-spring-docker/benchmarks/06-base-image-choice/results/raw.csv --baseline samples/java-spring-docker/benchmarks/06-base-image-choice/results/baseline.jsonDefault runtime: dockerfile generate with --recipe jvm-balanced (the default) uses runtime_image = distroless: a digest-pinned gcr.io/distroless/base-*:nonroot stage plus a jlink-built JVM and layered Spring Boot JAR — not a full OS image. Distroless images have no shell, so generated Dockerfiles omit HEALTHCHECK; configure readiness probes in Kubernetes or your orchestrator. OS runtime bases are compared in benchmark scenario 06 — see cli/README.md.
doctorchecks the project root and build tool.initwrites a starter config file.dockerfile generatewrites a Dockerfile to the requested path (default recipe: distroless + jlink layered JAR).benchmark generatecreates benchmark scenarios.benchmark runexecutes the benchmark runner.benchmark analyzeturnsraw.csvinto a table or JSON summary.
See cli/README.md for the command reference and config precedence rules.
See docs/benchmark-methodology.md for the benchmark model, run profiles, and summary calculations.
Benchmarks are an optional evidence subsystem and require benchmark extras (springdocker[benchmark]).
The sample project keeps benchmark scenarios under samples/java-spring-docker/benchmarks/.
Generated variant Dockerfiles and most run output are gitignored — regenerate with springdocker benchmark generate and springdocker benchmark run.
The scenario 06 CI regression baseline lives under samples/java-spring-docker/benchmarks/06-base-image-choice/results/.
See samples/java-spring-docker/benchmarks/README.md for the full artifact policy.
Authoritative list of generated scenarios under samples/java-spring-docker/benchmarks/.
Regenerate directories with springdocker benchmark generate. Scenario 07 is listed after 06 and before 08 by id, even though the generator emits 08 before the native scaffold.
| ID | Directory | Purpose | Variants | Further reading |
|---|---|---|---|---|
| 01 | 01-multi-stage-build-structure |
Multi-stage Dockerfile layout vs a simpler two-stage build | specialized-multi-stage, simple-two-stage |
benchmark-methodology.md |
| 02 | 02-buildkit-gradle-cache |
BuildKit cache mount on dependency rebuilds | with-buildkit-cache, without-buildkit-cache |
benchmark-methodology.md |
| 03 | 03-custom-jre-jlink |
jlink vs vendor JRE on debian-slim vs stock Temurin image | with-jlink-runtime, without-jlink-runtime, temurin-jre-image |
jvm-optimization.md, golden-samples.md |
| 04 | 04-jep483-aot-cache |
JEP 483 ahead-of-time class-loading cache (Java 24+) | with-aot-cache, without-aot-cache |
jvm-optimization.md · extra runs: 8 (quick) / 15 (full) |
| 05 | 05-jvm-container-flags |
Tuned vs baseline JVM container flags | tuned-flags, defaults-like |
jvm-optimization.md |
| 06 | 06-base-image-choice |
Runtime base image tradeoffs (jlink on every base) | alpine, debian-slim, ubuntu, distroless (configurable) |
benchmark-methodology.md · CI regression baseline |
| 07 | 07-native-benchmark |
Native-image scaffold (native-aot recipe); skipped by default |
(single Dockerfile at scenario root — no variant dirs) | native-image-roadmap.md |
| 08 | 08-appcds |
AppCDS shared archive at build time | with-appcds, without-appcds |
jvm-optimization.md |
Example analyze commands (after benchmark run):
springdocker benchmark analyze --project-root samples/java-spring-docker \
benchmarks/03-custom-jre-jlink/results/raw.csv --format table
springdocker benchmark analyze --project-root samples/java-spring-docker \
benchmarks/04-jep483-aot-cache/results/raw.csv --format json
springdocker benchmark compare --project-root samples/java-spring-docker \
benchmarks/03-custom-jre-jlink/results/raw.csv --baseline-variant with-jlink-runtimeCurrent reports focus on:
- image size
- build duration
- startup latency
- success rate
Benchmark summaries can be rendered as:
- terminal tables
- JSON
| Layer | CI / fixtures | Reference sample (samples/java-spring-docker/) |
|---|---|---|
| Python CLI | 3.10–3.12 tested in CI | — |
| Build tools | Maven and Gradle (minimal fixtures + examples) | Maven and Gradle |
| Spring Boot | Projects with Spring Boot markers | 4.0.1 |
| Java in generated Dockerfiles | ≥17 (generator validation) | 25 default in sample config |
The reference sample uses bleeding-edge versions to stress-test generator output and benchmark scenarios. Your project does not need to match those versions to use the CLI.
Documentation uses three status labels:
| Status | Meaning |
|---|---|
| Implemented | Shipped CLI behavior, runbooks, ADRs, or committed reference artifacts |
| Experimental | Scaffold or opt-in capability — documented with explicit limits; not production-ready |
| Roadmap | Planned capability or doc product not shipped yet |
docs/project-detection.md— Maven/Gradle detection boundaries and monorepo workflowsdocs/POSITIONING.md— product scope, CI guarantees, sample-tree strategydocs/digest-pin-runbook.mddocs/architecture.mddocs/benchmark-methodology.mddocs/golden-samples.mddocs/extensions.mddocs/security-hardening.mddocs/observability.mddocs/kubernetes.mddocs/adr/README.mddocs/multiarch.mddocs/onboarding.mddocs/team-adoption.mddocs/troubleshooting.mddocs/typing-roadmap.md— gradual mypy strictness and module rollout plandocs/jvm-optimization.mddocs/distribution.mddocs/presentation/README.md— Reveal.js decks: ownership, update cadence, commit/publish policydocs/example-gallery.md— index of committed Dockerfile and benchmark JSON examples underdocs/examples/docs/compatibility-matrix.md— descriptive support ranges (Java 17+, Python 3.10+, etc.)SECURITY.mdCONTRIBUTING.md
docs/native-image-roadmap.md—native-aotrecipe and scenario 07 scaffold; runner skips native by default; production native-image workflow remains roadmap
docs/benchmark-dashboard.md— standalone trend dashboard (presentation decks andbenchmark analyzeJSON are substitutes today)
| Tool | Focus | What springdocker adds |
|---|---|---|
| Jib | Dockerless image build | benchmark-aware Dockerfile and runtime tuning workflows |
| Buildpacks | Opinionated platform build | explicit Dockerfile generation and benchmark artifacts |
| Manual Dockerfiles | Full control | project detection, config, and repeatable benchmark analysis |
docs/golden-samples.md- fixture walkthroughs, CI coverage, and variant coveragesamples/java-spring-docker/README.md- full benchmark sample appsamples/java-spring-docker/HELP.mdsamples/java-spring-docker/k8s/kustomization.yamlsamples/java-spring-docker/tools/README.md
Clone the repository and use an editable install — see CONTRIBUTING.md. The main package is under src/springdocker/. Run pytest (≥80% line coverage gate), ruff check src tests, and mypy src before pushing changes.