Optimizing Spring Boot Fat JARs: Reducing Deployment Package Size by Separating Dependencies
This article explains how to shrink large Spring Boot fat JARs by extracting common dependencies into a shared lib directory and building lightweight business JARs, offering step‑by‑step Maven configurations for four optimization levels that dramatically reduce deployment size and improve update speed for both monolithic and microservice projects.
With the popularity of Spring Boot, developers often end up with single executable JAR files that can be hundreds of megabytes, making deployment and hot‑fix updates painfully slow, especially in microservice architectures where dozens of such JARs may total several gigabytes.
Overview
The guide presents a series of Maven‑based techniques to split a fat JAR into a small business JAR and a shared lib directory containing all third‑party dependencies, thereby reducing the size of the deployable artifact from hundreds of megabytes to a few hundred kilobytes.
Level 0 – Conventional Fat JAR Build
Typical configuration uses the Spring Boot Maven plugin without any dependency extraction.
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>Running mvn clean install produces a JAR of ~16 MB (in the demo) or tens to hundreds of megabytes in real projects, and the whole system may require transferring 1–2 GB for a full deployment.
Level 1 – Separate Dependencies into a lib Directory
Uses maven-dependency-plugin to copy all compile‑time dependencies into ${project.build.directory}/lib and configures the Spring Boot plugin to produce a JAR that contains only the application classes.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals><goal>copy-dependencies</goal></goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<excludeTransitive>false</excludeTransitive>
<stripVersion>false</stripVersion>
<silent>true</silent>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includes><!-- exclude all dependencies from the JAR --><include>null</include></includes>
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals><goal>repackage</goal></goals>
</execution>
</executions>
</plugin>The resulting business JAR is only ~150 KB, and the deployment consists of the tiny JAR plus the shared lib folder, allowing near‑instant transfers for hot fixes.
Level 2 – Merge All Modules’ Dependencies into a Single lib Directory
For multi‑module projects, the same dependency‑copy plugin is applied in each module, but the outputDirectory points to a common ../devops/lib path. The Maven JAR plugin adds a Class-Path entry to the manifest so the JVM loads the shared libraries automatically.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<useUniqueVersions>false</useUniqueVersions>
</manifest>
</archive>
</configuration>
</plugin>All micro‑service JARs end up around 150 KB each, while the shared lib directory stays under 300 MB, eliminating the need for the -Djava.ext.dirs=lib JVM argument.
Level 3 – Include Non‑Maven (system‑scope) Third‑Party SDKs
When a dependency is declared with system scope (e.g., a vendor SDK placed in the project), the Maven JAR plugin does not automatically add it to the manifest. The solution is to manually append the SDK JAR to the Class-Path entry via manifestEntries.
<manifestEntries>
<Class-Path>${jar-manifestEntries-classpath}</Class-Path>
</manifestEntries>Each module defines properties such as:
<properties>
<boot-jar-output>../devops</boot-jar-output>
<jar-manifestEntries-classpath>. lib/hik-sdk-1.0.0.jar</jar-manifestEntries-classpath>
</properties>
<dependencies>
<dependency>
<groupId>com.hik</groupId>
<artifactId>hik-sdk</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/hik-sdk-1.0.0.jar</systemPath>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>The generated manifest contains a Class-Path that starts with the SDK JAR followed by all Spring Boot dependencies, ensuring correct version loading without conflicts.
Final Result
All services share a common lib directory; total size is only a few hundred megabytes, dramatically speeding up first‑time deployments.
Individual service JARs are reduced to 150‑200 KB, enabling near‑instant hot‑fix updates.
Explicit Class-Path entries prevent version‑collision issues between micro‑services.
Even non‑Maven SDKs can be packaged and loaded correctly.
Special Tips
When a Maven dependency version changes, remember to copy the updated JAR into the shared lib directory; otherwise the services may still reference the old version.
Store the built lib and business JARs in a Git repository. Each release commit shows exactly which files changed, allowing you to transfer only the modified artifacts instead of the whole directory.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
