Spring Boot Packaging with Maven Profiles and a Shell Deployment Tool
This article explains how to use Maven profiles and the maven‑assembly‑plugin to create a zip package for a Spring Boot application, and provides a reusable shell script (shenniu_publish.sh) for deploying, starting, stopping, and managing the service on Linux.
This article shares a practical guide for packaging a Spring Boot application using Maven profiles and a custom shell script deployment tool, aiming to simplify the deployment workflow.
Configuring Profiles for Different Environments
Typically a project has multiple deployment environments such as development, testing, UAT, and production. Two ways to separate configuration files are demonstrated, with the focus on using Maven profiles to select the appropriate configuration directory.
<profiles>
<profile>
<id>node</id>
<properties>
<!-- parameters passed to scripts -->
<activeProfile>node</activeProfile>
<package-name>${scripts_packageName}</package-name>
<boot-main>${scripts_bootMain}</boot-main>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
...
</profiles>The id identifies the environment directory, properties expose values to other configuration files (e.g., ${package-name} ), and activeByDefault sets the default profile.
Using maven‑assembly‑plugin to Build a Release Zip
For a Spring Boot jar, the goal is to keep configuration files and third‑party dependencies outside the main jar and bundle everything into a zip for easy upload to Linux. The relevant Maven plugin configuration is shown below.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>${scripts_bootMain}</mainClass>
</manifest>
</archive>
<excludes>
<exclude>**/*.yml</exclude>
<exclude>**/*.properties</exclude>
<exclude>**/*.xml</exclude>
<exclude>**/*.sh</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>make-a-jar</id>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<descriptors>
<descriptor>${project.basedir}/src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>Key points:
mainClass : specifies the entry class, e.g., com.sm.EurekaServerApplication .
excludes : removes configuration files from the main jar so they can be packaged separately.
descriptor : points to assembly.xml which defines the zip structure.
assembly.xml Configuration
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
<id>${activeProfile}</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>${package-name}-${activeProfile}/lib</outputDirectory>
<unpack>false</unpack>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>${project.basedir}/src/main/profiles/${activeProfile}</directory>
<outputDirectory>${package-name}-${activeProfile}/conf</outputDirectory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/main/scripts</directory>
<outputDirectory></outputDirectory>
<includes>
<include>**/*</include>
</includes>
<fileMode>777</fileMode>
<directoryMode>777</directoryMode>
<filtered>true</filtered>
</fileSet>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>${package-name}-${activeProfile}/</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets>
</assembly>Important nodes:
formats : defines the archive type (zip, tar, etc.).
fileMode : sets executable permission (777) for the script on Linux.
filtered : allows Maven properties (e.g., ${package-name} ) to be substituted inside the script.
After configuring Maven, you can select the desired profile in IDEA and generate the zip package, as shown in the screenshots.
Sharing the shenniu_publish.sh Deployment Tool
The generated zip contains a shell script that can unzip, start, stop, and restart the Spring Boot jar. Its main features are:
Unzip the package and set execution permissions.
Start the jar using either java -cp or java -jar .
Stop the running process.
Restart the application.
#!/usr/bin/env bash
# Variable parameters
languageType="javac" # supported: java, javac, netcore
# Values passed from pom
baseZipName="${package-name}-${activeProfile}"
packageName="${package-name}"
mainclass="${boot-main}"
# Fixed variables
basePath=$(cd `dirname $0`/; pwd)
baseZipPath="${basePath}/${baseZipName}.zip"
baseDirPath="${basePath}"
pid=
function shenniu_unzip() {
echo "Unzipping..."
if [ ! `find ${baseZipPath}` ]; then
echo "Zip not found: ${baseZipPath}"
else
echo "Unzip destination: ${baseDirPath}/${baseZipName}"
unzip -od ${baseDirPath}/${baseZipName} ${baseZipPath}
chmod +x ${baseDirPath}/${baseZipName}/${packageName}
echo "Unzip completed."
fi
}
function getPid() {
echo "Checking status..."
pid=`ps -ef | grep -n ${packageName} | grep -v grep | awk '{print $2}'`
if [ ${pid} ]; then
echo "Running pid: ${pid}"
else
echo "Not running"
fi
}
function start() {
stop
if [ ${pid} ]; then
echo "Failed to stop, cannot start"
else
echo "Starting program..."
read -p "Enter program type (java,javac,netcore) [default: ${languageType}]: " read_languageType
if [ ${read_languageType} ]; then
languageType=${read_languageType}
fi
echo "Selected type: ${languageType}"
cd ${baseDirPath}/${baseZipName}
if [ "${languageType}" == "javac" ]; then
if [ ${mainclass} ]; then
nohup java -cp conf:lib/*.jar:${packageName}.jar ${mainclass} >${baseDirPath}/${packageName}.out 2>&1 &
fi
elif [ "${languageType}" == "java" ]; then
nohup java -jar ${baseDirPath}/${baseZipName}/${packageName}.jar >/dev/null 2>&1 &
elif [ "${languageType}" == "netcore" ]; then
nohup ${baseDirPath}/${baseZipName}/${packageName} >/dev/null 2>&1 &
fi
getPid
if [ ${pid} ]; then
echo "Started"
tail -n 50 -f ${baseDirPath}/${packageName}.out
else
echo "Start failed"
fi
fi
}
function stop() {
getPid
if [ ${pid} ]; then
echo "Stopping..."
kill -9 ${pid}
getPid
if [ ${pid} ]; then
echo "Stop failed"
else
echo "Stop succeeded"
fi
fi
}
if [ $# -ge 1 ]; then
case $1 in
"start") start ;;
"restart") start ;;
"stop") stop ;;
"unzip") shenniu_unzip ; start ;;
*) echo "${1} has no action" ;;
esac
else
echo "Commands:
unzip - unzip and start
start - start program
stop - stop process
restart - restart program
Example: ./shenniu_publish.sh start"
fiThe script receives package-name , activeProfile , and boot-main from Maven profiles, so no manual edits are required; only the Maven parameters need to change.
Running the Script on Linux
Upload the zip to a Linux host, unzip it, and execute the script:
unzip -od eureka-server-0.0.1-node eureka-server-0.0.1-node.zipIf the script was edited on Windows, convert line endings to Unix format using vim:
vim shenniu_publish.sh
set ff=unix
:wqAfter conversion, run ./shenniu_publish.sh start to launch the service. The stop and restart commands work similarly.
For further study, the full script and a sample Spring Cloud project are available at https://github.com/shenniubuxing3/springcloud-Finchley.SR2 .
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
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.