Passing JVM args to Docker image of Spring boot app on Kubernetes
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
Passing JVM arguments to a Spring Boot app on Kubernetes is easiest when you separate image design from deployment-time tuning. The image should start Java in a predictable way, and Kubernetes should inject the memory flags, GC options, and system properties that belong to a specific environment.
Prefer Environment-Driven JVM Options
A robust pattern is to let the JVM read options from JAVA_TOOL_OPTIONS. This is better than hardcoding production flags into the image because the same image can then run in different environments with different settings.
A simple Dockerfile can stay minimal:
Then inject JVM arguments from Kubernetes:
The JVM automatically reads JAVA_TOOL_OPTIONS, so you do not need a shell wrapper just to expand the variable.
Use args When You Want Container-Spec Control
Another valid pattern is to place the Java command in the image and override or extend arguments from the pod spec.
Then in Kubernetes:
This gives deployment manifests full control over the final Java invocation. The tradeoff is that the Kubernetes spec must now carry more of the startup command.
Match JVM Memory to Container Limits
Passing -Xmx without thinking about container limits is a common mistake. The JVM heap is only part of total memory usage. Metaspace, thread stacks, and native buffers also consume memory.
A safer rule is to align JVM settings with Kubernetes memory requests and limits instead of choosing heap sizes in isolation.
If the container limit is tight, an oversized heap can cause the pod to be killed even when the heap itself looks reasonable on paper.
Use Spring Properties Only Where They Belong
Some settings should be JVM arguments, and some should not. Examples:
- '
-Xmsand-Xmxare JVM flags' - '
-XX:+UseG1GCis a JVM flag' - '
-Dspring.profiles.active=prodis a Java system property' - '
SPRING_PROFILES_ACTIVE=prodis an application environment variable'
Do not push every application setting into JVM flags just because the Java command happens to be nearby. Spring Boot already reads many settings cleanly from environment variables and config files.
Avoid Image-Specific Magic Variables Unless You Control the Image
Many base images support helper variables such as JAVA_OPTS, but that behavior depends on the image entrypoint. If you do not control the startup script, JAVA_OPTS may do nothing at all.
That is why JAVA_TOOL_OPTIONS is often safer when you want image-agnostic JVM option injection. It is understood by the JVM itself, not by a particular Docker entrypoint convention.
Common Pitfalls
- Hardcoding environment-specific JVM flags into the Docker image.
- Setting
-Xmxwithout considering Kubernetes memory limits and non-heap memory. - Assuming
JAVA_OPTSworks in every Java image even when the entrypoint never reads it. - Putting the full Java command into a shell wrapper when simpler
ENTRYPOINTand environment patterns would be clearer. - Mixing JVM flags and ordinary Spring configuration without a clear boundary between them.
Summary
- Keep the image startup command simple and inject environment-specific JVM flags at deployment time.
- '
JAVA_TOOL_OPTIONSis a strong default because the JVM reads it directly.' - Kubernetes
argsare another valid option when you want manifest-level control of the command line. - Size heap settings with container memory limits in mind.
- Separate true JVM flags from ordinary Spring Boot configuration.
Related reading
- Passing long configuration file to Kubernetes
- Passing separate env variables to statefulset pods
- PersistentVolume does not use local host path
- PersistentVolumeClaim is stuck ''waiting for a volume to be created, either by external provisioner ebs.csi.aws.com'' on new AWS EKS cluster
- Passing NODE_ENV to docker to run package.json scripts
- Permission denied on accessing host directory in Docker
- Paste a multi-line Java String in Eclipse
- PathVariable in SpringBoot with slashes in URL

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.