CLI for Spring Boot Dockerfile and benchmark workflows (Maven/Gradle).
Project description
springdocker CLI
CLI for Spring Boot Dockerfile and benchmark workflows across Maven and Gradle projects.
Product scope and CI-evidenced guarantees: docs/POSITIONING.md.
Install
Primary path: install from PyPI and run against your Spring Boot project. Clone the repository only for benchmarks on the sample app or for development (ADR 0006).
pipx (recommended)
pipx install springdocker
springdocker --help
# optional: benchmark run/analyze (requires Docker)
pipx install 'springdocker[benchmark]'
Upgrade:
pipx upgrade springdocker
uv
uv tool install springdocker
uv tool upgrade springdocker
# benchmark extra
uv tool install 'springdocker[benchmark]'
pip
python3 -m pip install springdocker
python3 -m pip install 'springdocker[benchmark]'
From source (contributors)
git clone https://github.com/mnafshin/springdocker.git
cd springdocker
python3 -m pip install -e ".[dev]"
Quick usage
springdocker init --project-root samples/java-spring-docker --build-tool maven --profile quick
springdocker configure --project-root samples/java-spring-docker --force # interactive → writes [dockerfile] in config
springdocker dockerfile generate --project-root samples/java-spring-docker # non-interactive; reads .springdocker.toml
springdocker doctor --project-root samples/java-spring-docker
springdocker inspect --project-root samples/java-spring-docker --format json
springdocker explain --project-root samples/java-spring-docker Dockerfile.generated --format json
springdocker benchmark compare --project-root samples/java-spring-docker benchmarks/03-custom-jre-jlink/results/raw.csv --baseline-variant with-jlink-runtime --format json
springdocker dockerfile generate --project-root samples/java-spring-docker --output Dockerfile.generated --recipe jvm-balanced
springdocker dockerfile generate --project-root samples/java-spring-docker --recipe spring-aot
# native-aot emits experimental scaffold output only (not a production workflow)
springdocker dockerfile generate --project-root samples/java-spring-docker --recipe native-aot
springdocker benchmark generate --project-root samples/java-spring-docker --java-version 25
springdocker benchmark run --project-root samples/java-spring-docker --profile quick --runner-arg --skip-native
springdocker benchmark analyze --project-root samples/java-spring-docker benchmarks/04-jep483-aot-cache/results/raw.csv --format table
springdocker benchmark analyze --project-root samples/java-spring-docker benchmarks/04-jep483-aot-cache/results/raw.csv --format json --output benchmarks/04-jep483-aot-cache/results/summary.json
springdocker benchmark analyze --project-root samples/java-spring-docker benchmarks/04-jep483-aot-cache/results/raw.csv --fail-on-success-rate-below 95
springdocker benchmark analyze --project-root samples/java-spring-docker benchmarks/04-jep483-aot-cache/results/raw.csv --baseline benchmarks/04-jep483-aot-cache/results/baseline.json --fail-on-regression-above 20
Benchmark commands are optional evidence workflows and require benchmark extras.
Use samples/java-spring-docker/benchmarks/06-base-image-choice/results/baseline.json as the versioned CI regression baseline example (paired with committed raw.csv).
Scenario index: README.md.
Dockerfile recipes
| Recipe | Status | Default runtime | Notes |
|---|---|---|---|
jvm-balanced |
Supported | distroless + jlink | Default production-oriented JVM Dockerfile. |
spring-aot |
Supported | distroless + jlink | Spring Boot AOT processing on a JVM runtime. |
native-aot |
Scaffold only | distroless base (static binary) | Experimental GraalVM native-image Dockerfile output. Not a production-ready workflow; see docs/native-image-roadmap.md. |
The generator sets runtime_image = "distroless" internally for JVM recipes. That means distroless/base-debian* plus a copied jlink runtime — not the prebuilt distroless/java* image and not a full OS layer unless you change generator options (benchmark scenario 06 compares OS bases).
Runtime bases and HEALTHCHECK
| Runtime | Generator behavior |
|---|---|
distroless (default) |
Non-root, minimal base + jlink; no HEALTHCHECK (no shell/wget in the image). Probe readiness from the orchestrator (e.g. Kubernetes readinessProbe on /actuator/health/readiness). |
debian-slim, alpine, ubuntu, temurin |
Full OS or vendor JRE paths; HEALTHCHECK is emitted when Spring Boot Actuator is on the classpath. |
Supported runtime names: distroless, debian-slim, alpine, ubuntu, temurin (plus aliases such as debian-bookworm-slim, eclipse-temurin-jre). Set via [dockerfile].runtime_image in .springdocker.toml or --runtime-image on dockerfile generate.
Config-first workflow
.springdocker.toml is the single source of truth for Dockerfile generation (see ADR 0005). Team rollout guide: docs/team-adoption.md.
Command matrix
| Command | Interactive? | Writes config? | Writes Dockerfile? | Typical user |
|---|---|---|---|---|
springdocker init |
No | Yes (skeleton) | No | Platform / first checkout |
springdocker init --interactive |
Yes (via configure) | Yes | No | New service bootstrap |
springdocker configure |
Yes | Yes ([dockerfile]) |
Optional (--generate) |
Strategy changes |
springdocker dockerfile generate |
No | No | Yes | Daily dev + CI |
springdocker explain --config-aware |
No | No | No | Audit / review (advisory — not a CI gate) |
springdocker verify --check-config-drift |
No | No | No | CI SSOT gate (pass/fail) |
Precedence
| Priority | Source |
|---|---|
| 1 | CLI flags on dockerfile generate |
| 2 | Project .springdocker.toml |
| 3 | Built-in defaults |
Org policy (SPRINGDOCKER_POLICY) is planned (#123); not required today.
| Command | Purpose |
|---|---|
springdocker configure |
Interactive wizard that writes/updates [dockerfile] in config |
springdocker init --interactive |
Create config skeleton, then run configure |
springdocker dockerfile generate |
Deterministic generate from config (CI-safe, no prompts) |
Profiles (production-balanced, smallest-image, fast-cold-start, build-speed, simplest, compliance, custom) are selected in configure and expanded to explicit options in config.
dockerfile generate CLI overrides
Every [dockerfile] key is overridable from the CLI except file-backed keys (must_have_modules_file, jlink_baseline_modules), which stay in config only.
| Section | CLI flags | Config key(s) |
|---|---|---|
| General | --output, --java-version, --recipe, --profile |
output, java_version, recipe, profile |
| Runtime | --runtime-image, --non-root / --no-non-root, --platform-aware / --no-platform-aware, --healthcheck-path |
runtime_image, non_root, platform_aware, healthcheck_path |
| Build | --use-buildkit-cache / --no-use-buildkit-cache, --use-jlink / --no-use-jlink, --use-layered-jar / --no-use-layered-jar |
use_buildkit_cache, use_jlink, use_layered_jar |
| Supply chain | --include-oci-labels, --include-stopsignal, --include-embedded-sbom, --include-reproducible-controls, --pin-digests (each with --no-* form) |
matching include_* keys and pin_digests |
| JVM | --enable-appcds, --enable-jep483-aot-cache, --tuned-jvm-flags, --jvm-flag (repeatable) |
enable_appcds, enable_jep483_aot_cache, tuned_jvm_flags, jvm_flags |
Example one-off CI override:
springdocker dockerfile generate \
--project-root samples/java-spring-docker \
--runtime-image alpine \
--no-use-jlink \
--enable-jep483-aot-cache \
--no-include-embedded-sbom \
--pin-digests \
--jvm-flag "-XX:+UseZGC" # or --jvm-flag=-XX:+UseZGC when the flag starts with '-'
The dockerfile generate --help output groups flags under runtime, build, supply chain, and JVM sections.
The 07-native-benchmark scenario is generated with the native-aot scaffold recipe. The internal benchmark runner skips native scenarios by default (--skip-native).
Config file (.springdocker.toml)
All command resolvers use precedence:
- CLI flags
.springdocker.toml- defaults
Example:
[project]
build_tool = "maven"
[doctor]
build_tool = "maven"
[dockerfile]
output = "Dockerfile.generated"
java_version = 25
recipe = "jvm-balanced"
# profile = "production-balanced" # set by `springdocker configure`
# runtime_image = "distroless"
# use_jlink = true
# enable_jep483_aot_cache = false
# include_embedded_sbom = true
# pin_digests = true
# tuned_jvm_flags = true
# jvm_flags = ["-XX:MaxRAMPercentage=75", "-XX:+ExitOnOutOfMemoryError", "-Djava.io.tmpdir=/tmp"]
# Generator default runtime: distroless (gcr.io/distroless/base-* + jlink + layered JAR).
must_have_modules_file = "must-have.txt"
[benchmark.generate]
java_version = 25
legacy_scripts = false
[benchmark.run]
profile = "quick"
runner_args = ["--skip-native"]
cpuset_cpus = "0-1"
memory_limit = "2g"
warmup_runs = 1
max_workers = 1
normalized_runtime = true
legacy_scripts = false
When dockerfile.must_have_modules_file is set, springdocker reads modules from that file
(must-have.txt style, one module per line, # comments allowed) and injects them into
the jlink module list for reflection/dynamic-loading edge cases.
When jlink is enabled, springdocker auto-merges jlink baseline modules when Spring Web starters
are detected (spring-boot-starter-web, spring-boot-starter-webflux, spring-boot-starter-websocket):
java.desktop— JavaBeans and desktop-related APIs used by parts of the Spring Web stackjava.logging—java.util.loggingused by framework and library codejava.naming— JNDI lookups that jdeps often misses on web apps
Non-web Spring Boot workloads get no auto baseline — use jdeps plus must_have_modules_file for
extra modules. Override or disable in .springdocker.toml:
[dockerfile]
# Omit jlink_baseline_modules to auto-detect from Spring Web starters at generate time.
# Override defaults or set [] to disable baseline injection.
jlink_baseline_modules = ["java.desktop", "java.logging", "java.naming"]
See ADR 0007.
springdocker explain reports baseline and curated modules separately in JSON/table output.
Baseline modules are generator defaults; curated modules come from must_have_modules_file.
Create template config:
springdocker init --project-root samples/java-spring-docker --build-tool gradle
springdocker init --project-root samples/java-spring-docker --build-tool gradle --profile full --print
init --interactive
Creates .springdocker.toml if missing, then runs the same wizard as configure (no Dockerfile write unless you chain commands yourself):
springdocker init --project-root . --build-tool maven --interactive
# equivalent to:
# springdocker init --project-root . --build-tool maven
# springdocker configure --project-root . --force
Use --force on init to overwrite an existing skeleton; use configure --force to replace only the [dockerfile] section in an existing file.
See docs/team-adoption.md for first-time setup, CI examples, and migration from the retired tools/dockerfile_wizard.py.
Legacy benchmark scripts
Benchmark commands still support --use-legacy-scripts (or SPRINGDOCKER_LEGACY_SCRIPTS=1) to delegate to project scripts under benchmarks/ when needed:
springdocker benchmark generate --use-legacy-scripts ...
springdocker benchmark run --use-legacy-scripts ...
Dockerfile generation always uses the internal config-driven generator. Use springdocker configure for interactive setup instead of the retired tools/dockerfile_wizard.py script.
Inspect command
springdocker inspect prints static metadata about the target project:
- detected build tool
- Spring Boot version when present
- Java version when present
- direct dependency coordinates
- generated Dockerfile artifacts in the project root
- basic runtime compatibility guidance
Use --format json for machine-readable output.
Explain command
springdocker explain reads a Dockerfile and describes optimizations it recognizes using static text heuristics (regex and keyword matching).
Advisory only — not a CI gate. Explain output helps humans review and document a Dockerfile. It does not perform a security audit, lint the file, scan images, or prove runtime correctness. Hand-written Dockerfiles may be misread; a missing feature in explain output does not mean that optimization is absent at runtime.
Use springdocker verify for CI gates — hadolint, trivy, SBOM checks, optional dive/cosign/smoke, and --check-config-drift for config SSOT compliance. Only verify uses pass/fail semantics suitable for blocking merges.
Recognized signals include:
- multi-stage layout
- BuildKit cache usage
- jlink runtime stage
- non-root runtime
- tuned JVM flags
- jlink baseline modules (built-in defaults)
- curated must-have modules (from
must-have.txt)
Use --format json when you want stable structured output. The JSON notes field repeats the advisory scope and points to verify.
Add --config-aware to include resolved [dockerfile] options from .springdocker.toml, per-option sources (default or project), and drift detection against dockerfile generate. Config drift in explain is informational; enforce SSOT in CI with verify --check-config-drift:
springdocker explain --project-root . Dockerfile.generated --format json --config-aware
springdocker verify --project-root . --dockerfile Dockerfile.generated --check-config-drift
Verify command
springdocker verify runs a battery of checks against a generated Dockerfile and optional runtime context. It is designed to work in CI without installing every external tool.
springdocker verify --project-root tests/fixtures/maven-only Dockerfile.generated
springdocker verify --project-root tests/fixtures/maven-only Dockerfile.generated \
--image demo:latest \
--smoke-url http://127.0.0.1:8081/actuator/health \
--format junit \
--output reports/verify.junit.xml
springdocker verify --project-root . --dockerfile Dockerfile.generated --check-config-drift
--check-config-drift adds config SSOT checks when .springdocker.toml is present:
| Check | Validates |
|---|---|
config-drift |
Dockerfile matches dockerfile generate output for current config |
config-embedded-sbom |
/usr/share/sbom/spdx.json present when include_embedded_sbom = true |
config-non-root |
unprivileged USER when non_root = true |
config-jvm-flags |
configured JVM flags appear in ENTRYPOINT |
Built-in checks
| Check | Requires | Missing prerequisite | Check failure |
|---|---|---|---|
hadolint |
hadolint on PATH |
skipped (hadolint not installed) |
non-zero exit |
trivy |
trivy on PATH |
skipped (trivy not installed) |
HIGH/CRITICAL findings |
dive |
--image and dive on PATH |
skipped (no image provided or dive not installed) |
non-zero exit |
cosign |
--image and cosign on PATH |
skipped (no image provided or cosign not installed) |
non-zero exit |
sbom |
sbom.spdx.json in project root |
n/a (always runs) | failed if file missing, invalid JSON, or missing spdxVersion |
smoke |
--smoke-url |
skipped (no smoke URL provided) |
HTTP/network error or status ≥ 400 |
Verifier plugins registered under springdocker.verifiers run after the built-in checks. See docs/extensions.md.
Skip vs fail semantics
- skipped checks do not fail the command. They appear in table/JSON/JUnit/SARIF output for visibility.
- failed checks set the overall result to
failedand makespringdocker verifyexit with code1. - Only failed checks affect the exit code. A run where every external tool is missing but
sbom.spdx.jsonis valid still exits0.
Optional tools are intentionally optional: install hadolint, trivy, dive, and cosign locally or in CI when you want those gates enforced.
Supported --format values: table (default), json, junit, sarif, plus plugin-provided formats.
Security hardening
See docs/security-hardening.md for the runtime hardening defaults and recommended docker run flags.
Binary distribution
See docs/distribution.md for packaging notes and sample Homebrew, Scoop, standalone binary, and Docker runtime artifacts.
Multi-architecture builds
See docs/multiarch.md for the Buildx-friendly Dockerfile output and example multi-arch build command.
Compare command
springdocker benchmark compare compares each variant against a required baseline variant and reports deltas.
--baseline-variantselects the variant to compare against.--scenarionarrows the CSV to one scenario.--format jsonproduces machine-readable deltas.
Benchmark run reproducibility
springdocker benchmark run supports deterministic benchmark controls for local or CI runs:
--cpuset-cpuspins benchmark containers to specific CPUs.--memorycaps container memory.--warmup-runsexecutes discarded warmup probes before recording results.--max-workersruns standard scenarios concurrently with controlled worker count.--normalized-runtimeapplies read-only, no-new-privileges, and tmpfs isolation.
These settings can also come from [benchmark.run] in .springdocker.toml.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file springdocker-1.1.0.tar.gz.
File metadata
- Download URL: springdocker-1.1.0.tar.gz
- Upload date:
- Size: 85.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0f115ccaaac3645052357965db2bbd136cb1f497465643375648ff5e27468631
|
|
| MD5 |
f25a1cb332344b45a653fdee9955f38b
|
|
| BLAKE2b-256 |
88c4e5ce9b12417c860222271e23bca6f289193cce5cbf6cac91c6f5d672c633
|
Provenance
The following attestation bundles were made for springdocker-1.1.0.tar.gz:
Publisher:
release.yml on mnafshin/springdocker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
springdocker-1.1.0.tar.gz -
Subject digest:
0f115ccaaac3645052357965db2bbd136cb1f497465643375648ff5e27468631 - Sigstore transparency entry: 1875585013
- Sigstore integration time:
-
Permalink:
mnafshin/springdocker@df33b32b7eb2f4766b00dbbbe36a26b5fd179723 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/mnafshin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@df33b32b7eb2f4766b00dbbbe36a26b5fd179723 -
Trigger Event:
push
-
Statement type:
File details
Details for the file springdocker-1.1.0-py3-none-any.whl.
File metadata
- Download URL: springdocker-1.1.0-py3-none-any.whl
- Upload date:
- Size: 78.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5e9624ccb2d2c82180a0e1420391a665c3fb9c46fefd493374607b27181d46ac
|
|
| MD5 |
d1a260884283c7ca9a0b63e41e0fbd25
|
|
| BLAKE2b-256 |
5a7835f98ca42533a16a6e5da234ffcbe639be67b6d703740d360f35c3884459
|
Provenance
The following attestation bundles were made for springdocker-1.1.0-py3-none-any.whl:
Publisher:
release.yml on mnafshin/springdocker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
springdocker-1.1.0-py3-none-any.whl -
Subject digest:
5e9624ccb2d2c82180a0e1420391a665c3fb9c46fefd493374607b27181d46ac - Sigstore transparency entry: 1875585137
- Sigstore integration time:
-
Permalink:
mnafshin/springdocker@df33b32b7eb2f4766b00dbbbe36a26b5fd179723 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/mnafshin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@df33b32b7eb2f4766b00dbbbe36a26b5fd179723 -
Trigger Event:
push
-
Statement type: