Subsystem · docker

Docker

Curie generates a Dockerfile alongside your JAR — no plugin, no Buildpacks, no jib. The output is layer-friendly: dep JARs land in their own layer so app-code changes don't invalidate the dependency layer.

When it runs

curie build on an [application] project always produces the JAR. When Docker is in scope (curie run, or future curie docker commands), Curie writes a Dockerfile + .dockerignore next to the JAR in target/. Library projects ([library]) never produce a Dockerfile — they're not runnable on their own.

Configuration

Curie.toml
[application]
name      = "greeter"
version   = "0.1.0"
mainClass = "com.example.Greeter"

[docker]
baseImage = "eclipse-temurin:21-jre-alpine"

The only knob is baseImage. The default is eclipse-temurin:21-jre — a JRE-sized image is much smaller than a JDK image, and the runtime needs no compiler. Switch to -alpine for a smaller image, or to a distroless base for hardened deployments; whatever you choose is interpolated verbatim into the FROM line.

Generated Dockerfile

For an application with no dependencies, Curie writes:

target/Dockerfile
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY greeter-0.1.0.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

When the application has [dependencies], Curie copies the resolved dep JARs into target/libs/ at package time and adds a COPY libs/ libs/ step before the app JAR is copied. Docker caches each COPY as its own layer, so the dep layer is reused across rebuilds whenever your direct dependencies don't change — only the small app-JAR layer is rebuilt on a code edit.

target/Dockerfile (with deps)
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY libs/ libs/
COPY greeter-0.1.0.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

The JAR's Class-Path manifest entry points at libs/, so java -jar app.jar finds every transitive dep without a wrapper script.

.dockerignore

Curie also generates target/.dockerignore that excludes everything by default and re-whitelists only the files the build needs:

target/.dockerignore
*
!greeter-0.1.0.jar
!libs/

This keeps the build context small and stops accidental secrets in target/ (test outputs, intermediate class files) from being baked into the image.

Running

curie run defaults to Docker: it builds the image and launches a container in one step. Pass --no-docker to invoke java -jar directly against the host JRE — useful for quick local sanity checks where you don't care about the container.

$ curie run
  Compile         up to date
  Package         up to date
  Docker image    greeter:0.1.0  (via target/Dockerfile)
  Run             docker run --rm greeter:0.1.0

Hello from greeter.

Incremental Docker builds

The Dockerfile + .dockerignore are stamped against their inputs: the JAR mtime, the libs/ directory mtime, and Curie.toml's [docker] table. If none of those moved since the last image build, Curie skips the docker build invocation entirely.