# Dockerize a Spring Boot App

Yes. An agent writes a multi-stage Dockerfile for your Spring Boot app, adds it to the compose file beside its database, boots it in a sandbox and opens a PR.

[Run this on Rush](https://cloud.getrush.ai/run/dockerize-app/spring-boot/), in a Project that has your repository.

## What the agent does

The agent clones the repository into its own cloud sandbox and reads the build before it writes a Dockerfile. In a Spring Boot app like spring-projects/spring-petclinic the `README.md` says it outright: there is no `Dockerfile`, and the suggested route is `./mvnw spring-boot:build-image`. `pom.xml` inherits from `spring-boot-starter-parent` 4.1.0 with `java.version` set to 17, the Maven wrapper pins Apache Maven 3.9.16, and `build.gradle` mirrors it with a Java 17 toolchain and Gradle 9.5.1. `application.properties` defaults `database` to `h2`, exposes every actuator endpoint, and the `spring-boot-starter-actuator` dependency means `/actuator/health` already exists. The `docker-compose.yml` already defines two services, `mysql:9.7` and `postgres:18.4`, each named after the Spring profile that uses it, and `application-postgres.properties` reads `POSTGRES_URL`, `POSTGRES_USER` and `POSTGRES_PASS` with localhost defaults.

It also reads what must stay out of the image. `.gitignore` lists `target/` and `build/`, so the `.dockerignore` starts there and adds `.git`, the `k8s/` manifests and the `.devcontainer`. `spring-boot-docker-compose` and `spring-boot-testcontainers` are both `test` scope in `pom.xml`, so neither ships in the jar and the packaged app will not try to start Docker Compose from inside a container. `k8s/petclinic.yml` points at an image named `dsyer/petclinic`; the agent leaves that file alone and notes the name in the pull request, because an image tag is a deployment decision.

Then it writes the `Dockerfile` in two stages. The build stage starts from a JDK 17 image, copies `mvnw`, `.mvn/` and `pom.xml` first and runs `./mvnw -B dependency:go-offline` so the dependency layer caches apart from the source, then copies `src/` and runs `./mvnw -B package -DskipTests`. Tests are skipped in the image build only because they run in the next step, not because they are optional. The run stage starts from a JRE 17 image, creates a non-root user, uses Spring Boot's jar mode to extract the jar into layers so a code change does not invalidate the dependency layer, exposes 8080 and sets the entrypoint to `java` with the extracted launcher. A `HEALTHCHECK` calls `/actuator/health`, which the actuator starter already serves.

It wires the image into `docker-compose.yml` as an `app` service with `build: .`, `SPRING_PROFILES_ACTIVE=postgres`, `POSTGRES_URL=jdbc:postgresql://postgres/petclinic` with the user and password the existing `postgres` service declares, and `depends_on` the database. In the sandbox it runs `./mvnw -B verify` once so the tests pass before anything is containerised, then `docker compose up --build app`, waits for `/actuator/health` to report `UP`, requests `/owners/find` and `/vets.html` to see rendered pages rather than a stack trace, and keeps that output. Only then does it open the pull request.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Dockerize this Spring Boot app. Read README.md, pom.xml, build.gradle, docker-compose.yml, application.properties and the profile properties files first. Write a multi-stage Dockerfile that builds with the Maven wrapper, caches dependencies in their own layer, runs as a non-root user on a JRE image matching java.version, extracts the jar into layers, exposes 8080 and adds a HEALTHCHECK on /actuator/health. Add a .dockerignore based on .gitignore. Add an app service to docker-compose.yml that runs with the postgres profile against the existing postgres service. Run ./mvnw -B verify, then docker compose up --build app, wait for /actuator/health to report UP, request /owners/find and /vets.html, and keep the output. Do not change k8s/ or any Java source. Open a pull request with the commands you ran and their output.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a pull request on its own branch that adds `Dockerfile` and `.dockerignore` and changes `docker-compose.yml` to add the `app` service. No Java source, properties file or Kubernetes manifest changes. The description explains each stage of the `Dockerfile`, names the base images and why they match `java.version`, and quotes the `docker compose up` output: the Postgres container starting, the app logging that it started on port 8080, the health endpoint answering `UP` and the two page requests returning 200.

A reviewer checks three things before merging. That the build stage copies `pom.xml` before `src/` so the dependency layer caches. That the run stage has no JDK, no Maven and no source, only the extracted layers and a non-root user. And that `docker compose up app` on their own machine reproduces what the session shows, which it can, because the compose file now carries every environment variable the `postgres` profile reads.

The session's transcript keeps every command: `./mvnw -B verify` with the test summary, the image build with its layer output, and the curl calls against the running container. `GET /o/{org}/sessions/{id}/messages` returns it, so the evidence outlives the sandbox.

## Run it on every new service without a Dockerfile

When a new Spring Boot repository is created, a handler on your server receives GitHub's repository event and sends `POST /o/{org}/sessions` with this prompt and the new repository as the `resources` URL. The Rush GitHub App must already be installed on it, which `GET /o/{org}/github` confirms. Each run gets its own sandbox and opens its own pull request.

Set a `webhook_url` on the API key and Rush POSTs `run.completed` when the turn finishes, with the `session_url` to read the pull request from. A `run.failed` event is the one that needs a person; the rest need a reviewer.

## Why run it on Rush?

Dockerizing an app you did not write is a loop of build, wait, read a stack trace, fix, and build again, and each loop on a laptop downloads the same dependencies and competes with everything else you are doing. The agent runs that loop in its own sandbox with a real Docker daemon, a real Postgres from your own compose file and the app's own tests, and your machine does nothing.

The pull request arrives with the container already booted once and the health endpoint already answering, and the session keeps the output that proves it. Reviewing a Dockerfile against a transcript of it working is a different job from reviewing one against a hunch, and several services can get the same treatment at once, each in its own sandbox.

## Frequently asked questions

### Why not just use ./mvnw spring-boot:build-image, as the README suggests?

Buildpacks work and the agent says so in the pull request. A `Dockerfile` is what most CI systems, registries and reviewers expect to read, it builds without a Docker daemon on the build host when using BuildKit remotely, and it lets you choose the base image and the non-root user yourself. If you prefer the buildpack route, say so in the prompt and the agent adds the `docker-compose.yml` service around the image that goal produces instead.

### Maven or Gradle?

The repository has both wrappers. The agent builds with Maven because the `README.md` leads with `./mvnw` and the CI workflow `maven-build.yml` runs `./mvnw -B verify`, and it says so. Ask for Gradle and the build stage runs `./gradlew bootJar` instead; the run stage is the same.

### Which database does the compose service use?

Postgres, through the `postgres` profile, because `docker-compose.yml` already defines a `postgres` service and `application-postgres.properties` already reads `POSTGRES_URL`, `POSTGRES_USER` and `POSTGRES_PASS` from the environment. The image itself is database-neutral: run it with no profile and it uses the embedded H2 database, exactly as `./mvnw spring-boot:run` does.

### Which JDK does the image use?

One matching `java.version` in `pom.xml`, which is 17, for both stages: a JDK for the build and a JRE for the run stage. The `.devcontainer` asks for Java 21, and the pull request notes the difference so you can raise `java.version` deliberately rather than by accident of a base image.

### Does it change the Kubernetes manifests?

No. `k8s/petclinic.yml` names an image, `dsyer/petclinic`, that your registry decides. The agent leaves `k8s/` untouched and notes in the pull request that the manifest will need the new image name once you publish one.
