Question
After upgrading to Spring Boot 2.5.0, Gradle generates a myprogram-0.0.1-plain.jar file in addition to the usual myprogram-0.0.1.jar file. How can I configure Gradle 7.0.2 not to generate the *-plain.jar file?
Current output:
build/libs/
├── myprogram-0.0.1.jar
└── myprogram-0.0.1-plain.jar
Desired output:
build/libs/
└── myprogram-0.0.1.jar
Current build.gradle:
plugins {
id 'org.springframework.boot' version '2.5.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
Short Answer
You will learn why Spring Boot creates two JAR files, the difference between an executable Spring Boot JAR and a plain Java JAR, and how to disable the standard Gradle jar task when you only need the executable artifact.
Concept
Gradle's Java plugin provides a standard jar task. It creates a plain JAR containing your compiled classes and resources.
The Spring Boot Gradle plugin adds a bootJar task. It creates an executable JAR that contains:
- Your application classes
- Application resources
- Runtime dependencies
- Spring Boot launcher classes
Before Spring Boot 2.5, build setups could make both tasks try to use the same output name. In Spring Boot 2.5, the plugin avoids that collision by giving the standard jar task the plain classifier. Therefore, a project named myprogram can produce:
myprogram-0.0.1.jar executable Spring Boot JAR, produced by bootJar
myprogram-0.0.1-plain.jar standard Java JAR, produced by jar
If the project is an application that you only run as a Spring Boot service, the plain JAR is often unnecessary. You can disable the jar task and retain only the executable JAR.
This distinction matters because a plain JAR usually cannot be started with java -jar unless it has its own suitable manifest and all dependencies are otherwise available on the classpath. The Spring Boot executable JAR is designed to run directly.
Mental Model
Think of the two artifacts as two kinds of package:
- The plain JAR is a box containing only your product.
- The Spring Boot JAR is a ready-to-use kit containing your product, the required parts, and instructions for starting it.
Gradle builds the plain box through jar. Spring Boot builds the ready-to-use kit through bootJar.
Spring Boot 2.5 labels the plain box with -plain so the two packages do not accidentally overwrite each other.
Syntax and Examples
Disable Gradle's standard jar task in a Groovy build.gradle file:
jar {
enabled = false
}
Place it after the plugins block, commonly near the end of the build file:
plugins {
id 'org.springframework.boot' version '2.5.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
// Other configuration: group, version, repositories, dependencies...
jar {
enabled = false
}
Then run:
./gradlew build
The bootJar task remains enabled, so Gradle still creates the normal Spring Boot executable artifact:
build/libs/myprogram-0.0.1-SNAPSHOT.jar
You can also use task configuration syntax:
tasks.named('jar') {
enabled = false
}
Both approaches disable the same standard JAR task. The tasks.named form is especially useful in larger Gradle builds because it configures the task by name without eagerly looking up all tasks.
Step by Step Execution
Consider this configuration:
plugins {
id 'org.springframework.boot' version '2.5.0'
id 'java'
}
jar {
enabled = false
}
When you run the following command:
./gradlew build
Gradle proceeds as follows:
- The
javaplugin creates the standardjartask. - The Spring Boot plugin creates the
bootJartask. - Spring Boot configures the regular
jarartifact with theplainclassifier to prevent a filename conflict. - Your
jar { enabled = false }configuration marks the standardjartask as disabled. - The
buildlifecycle task runs compilation, tests, and artifact tasks it depends on. - Gradle skips the disabled
jartask. - Gradle runs
bootJarand writes the executable Spring Boot JAR tobuild/libs.
To inspect the available tasks, run:
./gradlew tasks --all
To build only the runnable application artifact, run:
./gradlew bootJar
Real World Use Cases
- Web APIs and backend services: A Spring Boot REST API is normally deployed as an executable JAR in a container, virtual machine, or process manager. The plain JAR is not needed.
- Docker images: A Dockerfile commonly copies one executable JAR into the image. Producing only that artifact prevents ambiguous wildcard copies such as
COPY build/libs/*.jar app.jar. - CI/CD pipelines: A release pipeline can publish, upload, or scan one known deployable artifact rather than selecting between two JAR files.
- Scheduled jobs and command-line tools: A Spring Boot batch job can be launched with
java -jar, so the executable JAR is the intended output. - Library modules: A reusable module is different: it typically needs the standard plain JAR for consumers to add to their classpath. In that case, do not disable
jar.
Real Codebase Usage
In real Gradle projects, application modules usually make the executable artifact explicit and avoid treating every generated file as deployable.
A typical application module configuration is:
plugins {
id 'org.springframework.boot'
id 'java'
}
jar {
enabled = false
}
Teams also commonly build the specific artifact required by deployment:
./gradlew clean bootJar
This avoids relying on incidental outputs from the broader build task.
In a multi-module repository, the decision is made per module:
- Application module: enable
bootJar; usually disablejar. - Shared library module: enable
jar; usually do not apply the Spring Boot plugin.
If a library module uses Spring Boot dependency management or Spring APIs, it can still publish a normal JAR. It should not automatically be packaged as a runnable application merely because it uses Spring-related dependencies.
Common Mistakes
Deleting the file after every build
Manually deleting *-plain.jar works temporarily, but the next build recreates it. Configure the producing task instead:
jar {
enabled = false
}
Disabling bootJar instead of jar
This configuration removes the executable Spring Boot artifact, which is usually the opposite of the goal:
// Usually wrong for a deployable Spring Boot application
bootJar {
enabled = false
}
Use this only when the project is intentionally a library and should not produce an executable Boot JAR.
Assuming the plain JAR is a duplicate
The files contain different layouts and serve different purposes. The plain JAR contains application classes and resources; the Boot JAR also packages runtime dependencies and startup support.
Renaming artifacts to hide the issue
Changing archiveFileName may rename output files, but it does not stop the regular jar task from running. Disable jar when you do not need its artifact.
Disabling jar in a publishable library
If the module is consumed by other projects or published to Maven repositories, disabling jar can leave no normal library artifact to publish. Keep jar enabled for library modules.
Comparisons
| Aspect | jar task | bootJar task |
|---|---|---|
| Main purpose | Package a standard Java library/application JAR | Package a runnable Spring Boot application |
| Typical output in Spring Boot 2.5 | app-version-plain.jar | app-version.jar |
| Includes runtime dependencies | No | Yes, in Spring Boot's executable layout |
Normally runnable with java -jar | No | Yes |
| Best suited to | Shared libraries and classpath-based applications | Services, APIs, jobs, and deployable applications |
| Disable for a Boot-only app? | Usually yes | No |
The plain part is a classifier: an extra label Gradle adds to distinguish an artifact from another artifact with the same base name and version.
Cheat Sheet
// Disable the standard Java JAR task.
jar {
enabled = false
}
// Equivalent lazy task configuration.
tasks.named('jar') {
enabled = false
}
# Build only the executable Spring Boot JAR.
./gradlew bootJar
# Run all normal build checks and produce configured artifacts.
./gradlew build
jarcreates the standard Java JAR.bootJarcreates the executable Spring Boot JAR.- Spring Boot 2.5 uses the
plainclassifier for the standard JAR to avoid filename collisions. - Disable
jarfor an application-only module when only the Boot artifact is required. - Keep
jarenabled when the module is a reusable or published library.
FAQ
Why does Spring Boot 2.5 create a -plain.jar file?
Spring Boot 2.5 distinguishes the standard Gradle jar output from the executable bootJar output. The plain classifier prevents both tasks from generating files with the same name.
Can I safely delete the plain JAR file?
You can delete it, but it will return on the next build. Disable the Gradle jar task if your application never needs the file.
What Gradle configuration removes the plain JAR?
Add this to build.gradle:
jar {
enabled = false
}
Will disabling jar stop my Spring Boot application from building?
No. The Spring Boot executable artifact is produced by bootJar, not the standard jar task.
Which JAR should I deploy to run a Spring Boot application?
Deploy the JAR without the -plain suffix, unless your build has been customized to use different names. It is normally the executable Spring Boot JAR.
Should a shared library disable the jar task?
No. Shared libraries generally need the normal JAR so other applications can depend on and use them.
Can I build the executable artifact without running the whole build lifecycle?
Yes:
./gradlew bootJar
This directly runs the task that creates the executable Spring Boot JAR.
Mini Project
Description
Configure a small Spring Boot application so its Gradle build produces only the executable application JAR. This mirrors a deployment-oriented service where a CI pipeline or Docker image needs one unambiguous artifact.
Goal
Build the application and verify that build/libs contains the executable Spring Boot JAR but no -plain.jar file.
Requirements
- Create or use a Spring Boot project that applies the
javaandorg.springframework.bootplugins. - Keep the Spring Boot executable JAR enabled.
- Disable the standard Gradle
jartask. - Run the Gradle
buildtask. - Verify that no file ending in
-plain.jaris generated inbuild/libs.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.