It's been a decade since all major Linux distributions adopted systemd as their init system. See https://en.wikipedia.org/wiki/Systemd#Adoption for some information about the adoption.
Spring Boot still supports SysVinit, which is at this point quite archaic, and this support comes with some complexity as evident by the amount of documentation that describes it - including some notable shortcomings of prepending script to a JAR.
Additionally, launch script integration tests are quite complex and resource intensive and take a lot of time when building the project while objectively providing little value considering that shouldn't be the way to run Spring Boot applications in 2025. See this breakdown of build tasks as an example.
This is a formal request to deprecate, and eventually remove, support for SysVinit. There has been some previous mention of this in https://github.com/spring-projects/spring-boot/issues/28453#issuecomment-1325171018.
Comment From: philwebb
We discussed this today and we'd like to remove the ability to prepend a launch script entirely. It's an issue that causes problems with tools and it goes against our recent efforts to move towards unpacking jars for performance gains.
Comment From: garretwilson
A major benefit of a prepended launch script is that one can distribute a Linux Java command that will just run, with no installation needed other than having a JVM installed on the system. This is extremely useful for making utility CLIs in Java. Otherwise we are stuck with the enormous overhead (and huge files) of something like Rust.
Let me see if I understand this decision correctly. If I want to distribute a foobar command, written in Java, to run on Linux:
Current Situation:
- I distribute a single executable
foobarfile. - The user places the
foobarin the user application directory. - The user executes
foobar.
After This Decision
- I distribute a
foobar.zipfile. - The user extracts
foobar.zipinto some directory that contains shell scripts and JAR files. - The user adds some symlink from the application directory to some main shell script inside the new
foobarextracted contents. - The user executes
foobar.
Now suddenly distributing (or upgrading!) a simple CLI written in Java becomes as much a chore as installing Maven or Tomcat!
Comment From: vpavic
Launch script provided by Spring Boot takes on a lot of concerns that are better off handled by init system since the main use case for it has been installing/running Spring Boot applications as system services.
What you're describing sounds like a different concern and that is packaging CLI Linux applications. There are ways simpler that relying on a Spring Boot prepended script to achieve that, like using Gradle's application plugin (not sure what's Maven equivalent).
Comment From: snicoll
+1 to Gradle's application plugin. Maven has the assembly plugin (and perhaps others). What you've described @garretwilson isn't a great fit for a CLI, IMO. Especially for those you'd want to bypass the bootloader and start a quickly as possible. With the infrastructure in place, this would be
- I distribute a single foobar.zip file.
- The user unzip foobar.zip wherever they want to (for instance in the user application directory).
- The user executes
<directory>/bin/cli.sh.
Comment From: garretwilson
What you've described isn't a great fit for a CLI.
But it's working great for me. I've worked for years (maybe a decade) on a project root POM that allows you, just by adding a property to the POM, to generate both a Windows .exe and a Linux self-executing JAR file. You run mvn package and it creates the executables and even bundles both the Windows and Linux executables in a Zip file. See for example https://www.jordial.com/software/datimprint/install .
It's working great. The user just puts a single executable file wherever they want to, whether on Windows or Linux. Why complicate things?
Maven has the assembly plugin …
Yes, that is the "easy" response everyone gives, even if they haven't actually tried it. The old Maven approaches (assembly plugin and the shade plugin) have numerous gotchas such as service files overwriting each other, signature files, the inability specify a descriptor in a parent POM, etc. It's a mess. See for example my own tickets to fix my own root POM:
- JAVA-256: Exclude signature files from executable JAR.
- JAVA-286: Automate generation of CLI assembly.
- JAVA-257: Switch to Sprint Boot Maven Plugin for generating executable JAR.
@snicoll the things you're referring me to have to do with bundling. But this Spring Boot issue is about removing the launch script. Are you saying that the Spring Boot bundler itself is going away too? If that's not what you're saying, then I'm not understanding your purpose for directing me to other bundler solutions.
Comment From: wilkinsona
We're a small team and we have to focus our efforts carefully and a new major release gives us a chance to do so. Unfortunately, support for embedded launch script isn't something that we have the resources to maintain in the long-term. Bash isn't a strength of the team and there were problems with the script that we were unable to address. That, coupled with the ongoing cost of maintaining and testing the current support means that it's the right time to remove support.
We've used Gradle's application plugin for some internal CLIs and it has served us well. Alternatively, Boot's own CLI doesn't use the embedded launch script yet it still offers easy installation on multiple platforms. Boot's CLI is built with Gradle now but was built in the past using Maven's Assembly plugin with same end result from a user's perspective.
Comment From: garretwilson
@wilkinsona and everyone, thanks for your comments and for listening to my concerns. I can understand the stretched resources.
But can you clarify if the Maven spring-boot-maven-plugin that bundles the everything into a self-contained JAR is going away, or just support for the launch script. Some of the comments in this ticket seem to refer to the bundling mechanism itself. Going forward, will I still be able to create an "uber" JAR, executable manually by using java -jar …?
If I can at least continue bundling an application in to a self-contained JAR runnable by java -jar …, maybe I can look at your source code for the removed launch script functionality, and create my own plugin to add the launch script after the executable JAR has been produced by your plugin.
Comment From: philwebb
@garretwilson The uber jar support will remain and java -jar will continue to work. Commit 81aa674adb11b0155823d4a3575edfa0c6d72b9f removed the code if you want to work backwards from that.
Comment From: wildcart
While investigating what is required for us to upgrade to Spring Boot 4 I stumbled across this issues, as we're also using the Maven plugin to build 'single file' command line application. It took me a while (until I found this thread) to understand that the reason our build was broken, wasn't a misconfiguration of the build but that this feature has been removed.
Would it be possible / a good idea to add a comment to the documentation? Something like:
Note: previously this plugin supported the
executableflag which prepended a 'launch script' to the JAR file, which allowed to directly run the JAR from the command line, without specifyingjavar -jar. This option has been removed in Spring Boot 4.
Comment From: wildcart
@garretwilson I've 'fixed' our build by using the Exec Maven Plugin to prepend the 'launch script' to the JAR file created by the Spring Boot Plugin.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.6.2</version>
<executions>
<execution>
<id>create-executable-jar</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>{project.directory}/contrib/prepend-launchscript</executable>
<arguments>
<argument>${project.build.directory}/filtered-resources/launch-jar.sh</argument>
<argument>${project.build.directory}/${jar-file}</argument>
<argument>${project.build.directory}/${app-name}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
The prepend-launchscript is a simple wrapper for cat, which is required as I couldn't figure out how to redirect output using the Exec plugin:
#!/usr/bin/env sh
cat "$1" "$2" > "$3"
Our 'launch script' does quite a lot of things, but the simplest form could be:
#!/usr/bin/env bash
JAR_FILE=$(realpath "${BASH_SOURCE[0]}")
java -jar ${JAR_FILE}
This solution works for us on Linux. I haven't tested Mac OS or Windows as we're targeting neither platform. Mac OS probably works, I guess, but I have no idea how to achieve this on Windows.
Comment From: snicoll
@wildcart Thanks for trying the milestones.
This is already described in the release notes for RC1.
It took me a while (until I found this thread) to understand that the reason our build was broken, wasn't a misconfiguration of the build but that this feature has been removed.
Trying to use the executable argument with Spring Boot 4 leads to:
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[WARNING] Parameter 'excutable' is unknown for plugin 'spring-boot-maven-plugin:4.0.0-RC2:repackage (repackage)'
Comment From: garretwilson
I've 'fixed' our build by using the Exec Maven Plugin ...
@wildcart thanks for trying, but really you're just calling some shell program in the file system which does the work. Beyond the cross-platform issues, we configure this functionality in a parent POM which is simply added as a dependency and downloaded as part of the build process. The parent POM would have to use yet another plugin to generate the shell script and make it executable, and do that in some phase before the Exec Maven Plugin. It's probably doable but would wind up being a huge, ugly hack.