Backend Development 16 min read

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.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Spring Boot Packaging with Maven Profiles and a Shell Deployment Tool

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"
fi

The 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.zip

If the script was edited on Windows, convert line endings to Unix format using vim:

vim shenniu_publish.sh
set ff=unix
:wq

After 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 .

backendDeploymentmavenSpring Bootshell scriptProfilesAssembly Plugin
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.