EthicsComputerCourses
Join training +91 90532 10052

Build & Run the Jar

Spring Boot · Lesson 25 of 29 Advanced 7 min read Updated

During development you start the app from your IDE or with ./mvnw spring-boot:run. On a server there is no IDE and usually no source code: you copy one file and run it. Spring Boot's executable jar contains your classes, every library and the embedded Tomcat, so the only thing the server needs is a Java runtime.

In this lesson you will build the jar, look inside it, run it with configuration options, use the tools jar mode that prepares it for containers, and run it as a Linux service that shuts down gracefully.

Building the jar

Terminal
./mvnw clean package
Output
[INFO] Building jar: C:\...\academy\target\academy-0.0.1-SNAPSHOT.jar
[INFO] Replacing main artifact C:\...\academy\target\academy-0.0.1-SNAPSHOT.jar with repackaged archive, adding nested dependencies in BOOT-INF/.
[INFO] BUILD SUCCESS

(Paths shortened.) clean deletes the old target folder; package compiles, runs all tests, builds a normal jar, and then the Spring Boot Maven plugin repackages it into an executable one. If a test fails, no jar is built, which is exactly what you want. -DskipTests skips the tests; use it rarely, never as a habit to hide failures. With Gradle, ./gradlew build puts the jar in build/libs.

The academy jar is about 55 MB, because it carries everything it needs.

Inside the jar

A jar is a zip file. jar tf lists its contents (the academy jar has 256 entries); the important ones are:

Text
META-INF/MANIFEST.MF                                   says which class starts the jar
org/springframework/boot/loader/launch/JarLauncher.class   Spring Boot's launcher
BOOT-INF/classes/application.properties                your resources
BOOT-INF/classes/data.sql
BOOT-INF/classes/in/cricket/academy/...                your compiled classes
BOOT-INF/lib/spring-boot-4.1.2.jar                     every dependency, as nested jars
BOOT-INF/lib/h2-2.4.240.jar
BOOT-INF/classpath.idx                                 order of the nested jars
BOOT-INF/layers.idx                                    layer information for containers

Java cannot normally load jars nested inside a jar. Spring Boot's JarLauncher can: the manifest names it as the Main-Class, it sets up a class loader for BOOT-INF/lib, and then calls your AcademyApplication.main. This is why the jar runs with plain java -jar.

Running it

Terminal
java -jar target/academy-0.0.1-SNAPSHOT.jar
java -jar target/academy-0.0.1-SNAPSHOT.jar --server.port=9000 --spring.profiles.active=prod
java -Xmx512m -jar target/academy-0.0.1-SNAPSHOT.jar
  • Arguments after the jar name with -- are Spring properties, with the highest priority, as you saw in Configuration.
  • Options before -jar go to the JVM: -Xmx512m limits the heap to 512 MB.
  • Environment variables (SPRING_PROFILES_ACTIVE=prod, SPRING_DATASOURCE_PASSWORD=...) work too, and are the usual way on servers.
  • An application.properties file in the folder you start from overrides the one inside the jar, which is handy for server-specific settings.

Remember the "two Javas" problem from Prerequisites & Setup: the server's java -version must be 17 or later, matching what you compiled for.

The tools jar mode

Spring Boot jars contain a small built-in tool. Run it with -Djarmode=tools:

Terminal
java -Djarmode=tools -jar target/academy-0.0.1-SNAPSHOT.jar help
java -Djarmode=tools -jar target/academy-0.0.1-SNAPSHOT.jar list-layers
Output
Usage:
  java -Djarmode=tools -jar application.jar

Available commands:
  extract      Extract the contents from the jar
  list-layers  List layers from the jar that can be extracted
  help         Help about any command
dependencies
spring-boot-loader
snapshot-dependencies
application

The four layers are groups of files that change at different speeds: third-party libraries rarely change, your own code changes on every build. extract unpacks the jar into a faster-starting layout, with one folder per layer:

Terminal
java -Djarmode=tools -jar application.jar extract --layers --destination extracted
Output
extracted/application/application.jar
extracted/dependencies/lib/        (84 library jars)
extracted/snapshot-dependencies/
extracted/spring-boot-loader/

(This listing was made with find after the command, which itself prints nothing.) The next lesson uses exactly these folders in a Dockerfile, so that a code change rebuilds only the small application layer of the image.

Running as a Linux service

On a plain Linux server (a VPS), let systemd start the app at boot and restart it if it crashes. Create /etc/systemd/system/academy.service:

Text
[Unit]
Description=Pune Cricket Academy API
After=network.target

[Service]
User=academy
WorkingDirectory=/opt/academy
EnvironmentFile=/opt/academy/academy.env
ExecStart=/usr/bin/java -Xmx512m -jar /opt/academy/academy.jar
SuccessExitStatus=143
Restart=on-failure

[Install]
WantedBy=multi-user.target
Terminal
sudo systemctl daemon-reload
sudo systemctl enable --now academy
sudo journalctl -u academy -f

The app runs as an unprivileged academy user, secrets come from an environment file readable only by that user, and journalctl shows the console log. Exit status 143 is what Java returns when stopped by SIGTERM, so systemd treats a normal stop as success. Put a reverse proxy such as Nginx in front to handle HTTPS, as shown in the Docker course. The Linux tutorial covers systemd and users in more depth.

Graceful shutdown and other runtime settings

  • Graceful shutdown is on by default (server.shutdown=graceful): on SIGTERM, Tomcat stops accepting new requests and lets running ones finish, for up to 30 seconds (spring.lifecycle.timeout-per-shutdown-phase). Deployments then do not cut off users mid-request.
  • Virtual threads: on Java 21 or later, spring.threads.virtual.enabled=true makes Tomcat handle each request on a lightweight virtual thread, which helps APIs that spend most of their time waiting for databases or other services.

Common mistakes

  • Running the plain jar. With Gradle, build/libs also contains ...-plain.jar, which is not executable; run the other one.
  • Old Java on the server. UnsupportedClassVersionError means the server's Java is older than the version you compiled for.
  • Secrets on the command line. Arguments are visible to other users in the process list; use an environment file with tight permissions.
  • Skipping tests to "save time" and deploying a broken build. Let package run them.

Key points

  • ./mvnw clean package runs tests and builds an executable jar in target/; Gradle uses ./gradlew build and build/libs.
  • The jar contains your classes in BOOT-INF/classes, libraries in BOOT-INF/lib, and Spring Boot's JarLauncher.
  • Run it with java -jar; --key=value arguments and environment variables configure it.
  • java -Djarmode=tools -jar app.jar extract --layers splits it into dependencies, spring-boot-loader, snapshot-dependencies and application layers for containers.
  • Use systemd (or containers) to run it as a service; graceful shutdown is the default.

Exercise

You must deploy academy.jar on an Ubuntu server with 2 GB of RAM. The database password must not appear in any file inside the jar or on the command line, the app must use the prod profile and port 8081, and it must restart automatically after a crash. Write the environment file and the ExecStart line, and list the commands to start the service and watch its log.

Show solution
Text
# /opt/academy/academy.env   (owner academy, permissions 600)
SPRING_PROFILES_ACTIVE=prod
SERVER_PORT=8081
SPRING_DATASOURCE_PASSWORD=<the real password>
Text
ExecStart=/usr/bin/java -Xmx768m -jar /opt/academy/academy.jar
Restart=on-failure
EnvironmentFile=/opt/academy/academy.env
Terminal
sudo chmod 600 /opt/academy/academy.env
sudo systemctl daemon-reload
sudo systemctl enable --now academy
sudo journalctl -u academy -f

Spring Boot maps SPRING_PROFILES_ACTIVE, SERVER_PORT and SPRING_DATASOURCE_PASSWORD to the matching properties through relaxed binding. -Xmx768m leaves memory for the operating system and the database driver's buffers on a 2 GB machine.

Quick Quiz

1. What does ./mvnw package do if a test fails?

2. Where are your compiled classes inside a Spring Boot jar?

3. In java -Xmx512m -jar app.jar --server.port=9000, which part is a Spring property?

4. What does graceful shutdown do?

Learn Spring Boot with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call