Notes on Troubleshooting Java Container Images Created with Paketo Buildpack
Java container images created with mvn spring-boot:build-image/gradle bootBuildImage or Paketo Buildpack use JRE by default and do not include JDK. Therefore, diagnostic tools such as jstack and jcmd are not included in the container.
Additionally, recent Spring Boot uses Paketo Noble Java Tiny Builder as the Builder.
The Stack of this Builder is ubuntu-noble-run-tiny, which is an image with minimal necessary files added to scratch.
In other words, this image does not include shells such as bash or sh.
This is necessary for image lightweighting and multi-architecture support, but it can be inconvenient during troubleshooting.
You'd want to be able to troubleshoot while using this Builder, right? We want to be able to take stack traces and heap dumps with jcmd.
This is where Paketo Buildpack for Jattach comes in handy.
jattach is a lightweight command-line tool for dynamically connecting to and performing operations on running Java processes. The functionality of jmap, jstack, jcmd, and jinfo is combined into a single binary. Paketo Buildpack for Jattach is a Buildpack that adds this jattach to container images.
Paketo Buildpack for Jattach is not enabled by default, but can be enabled by setting the build-time environment variable BP_JATTACH_ENABLED to true.
For Maven, the following configuration will include jattach in the image.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<env>
<BP_JATTACH_ENABLED>true</BP_JATTACH_ENABLED>
</env>
</image>
</configuration>
<!-- ... -->
</plugin>
Now you can use jattach within the container to take stack traces and heap dumps. The path to jattach is /layers/paketo-buildpacks_jattach/jattach/bin/jattach.
The usage of jattach is as follows.
NAMESPACE=...
POD_NAME=...
kubectl exec -n $NAMESPACE $POD_NAME -ti -- /layers/paketo-buildpacks_jattach/jattach/bin/jattach help
You will get output like the following.
jattach 2.2 built on Jan 10 2024
Usage: jattach <pid> <cmd> [args ...]
Commands:
load threaddump dumpheap setflag properties
jcmd inspectheap datadump printflag agentProperties
command terminated with exit code 1
Below is an example of taking a heap dump within a Kubernetes Pod. The PID of the Java process is 1.
NAMESPACE=...
POD_NAME=...
kubectl exec -n $NAMESPACE $POD_NAME -ti -- /layers/paketo-buildpacks_jattach/jattach/bin/jattach 1 dumpheap /tmp/heapdump.hprof
You should see log output like the following.
Connected to remote JVM
JVM response code = 0
Dumping heap to /tmp/heapdump.hprof ...
Heap dump file created [69843865 bytes in 0.294 secs]
This saves the heap dump to /tmp/heapdump.hprof.
Now let's copy this heap dump file locally with the following command.
kubectl cp -n $NAMESPACE $POD_NAME:/tmp/heapdump.hprof ./
However, you get an error like the following and cannot copy it. This is because the tar command does not exist within the image.
time="2025-12-18T09:58:08Z" level=error msg="exec failed: unable to start container process: exec: \"tar\": executable file not found in $PATH"
command terminated with exit code 255
Let's try using the cat command to output the heap dump to standard output and receive it locally instead.
kubectl exec -n $NAMESPACE $POD_NAME -- cat /tmp/heapdump.hprof > ./heapdump.hprof
However, again you get an error like the following and cannot copy it. This is because the cat command also does not exist within the image.
time="2025-12-18T10:00:09Z" level=error msg="exec failed: unable to start container process: exec: \"cat\": executable file not found in $PATH"
command terminated with exit code 255
As you can see, container images created using Paketo Noble Java Tiny Builder do not include shells, so you cannot copy files with kubectl cp or kubectl exec.
So what should you do? Actually, JRE has a lesser-known command called jwebserver. jwebserver is a small HTTP server that can serve specific directories.
Use the following command to start jwebserver within the Pod and serve the /tmp directory on port 8000.
kubectl exec -n $NAMESPACE $POD_NAME -ti -- /layers/paketo-buildpacks_bellsoft-liberica/jre/bin/jwebserver -b 0.0.0.0 -p 8000 -d /tmp
In another terminal, forward local port 8000 to the Pod's port 8000.
NAMESPACE=...
POD_NAME=....
kubectl port-forward -n $NAMESPACE $POD_NAME 8000:8000
In yet another terminal, download the heap dump file with the wget command.
wget http://localhost:8000/heapdump.hprof
Now you have successfully saved the heap dump file locally.
Java container images created with Paketo Buildpack do not include diagnostic tools by default, but you can add the jattach command by setting the BP_JATTACH_ENABLED=true environment variable.
Also, images using Paketo Noble Java Tiny Builder do not include shells, so the usual kubectl cp command cannot be used, but by utilizing jwebserver included in JRE, file downloads become possible.
This method allows you to enjoy the benefits of lightweight container images while being able to obtain diagnostic information such as heap dumps and stack traces when needed.