Question

Create an Executable JAR with Dependencies in Maven

javabuildmaven-2executable-jarbuild-automation

Question

I want to package my Java project as a single executable JAR for distribution.

How can I configure a Maven project so that all dependency JARs are included in the final output JAR, making it directly runnable?

Short Answer

By the end of this page, you will understand how Maven creates JAR files, why dependencies are usually not bundled by default, and how to build a single runnable JAR that includes your application's dependencies. You will also learn the most common Maven plugins used for this task, when to use each one, and how to avoid common packaging mistakes.

Concept

In Maven, the default jar packaging creates a JAR containing only your project's compiled classes and resources. It does not automatically unpack and merge dependency JARs into that file.

That is why a normal Maven-built JAR often fails when run alone with:

java -jar myapp.jar

if your application depends on external libraries.

To distribute a Java application as a single runnable file, you usually need an uber JAR or fat JAR. This is a JAR that contains:

  • your compiled classes
  • your resources
  • classes and resources from dependencies
  • a manifest entry pointing to the main class

This matters because real applications almost always depend on libraries such as logging frameworks, HTTP clients, JSON parsers, database drivers, and utility packages. If those dependencies are missing at runtime, the application will fail with errors like:

NoClassDefFoundError
ClassNotFoundException

In Maven, the two most common ways to build a runnable JAR with dependencies are:

  • Maven Shade Plugin: the most common and flexible option for creating a single merged JAR
  • Maven Assembly Plugin: can also create a JAR with dependencies, often simpler but less flexible for advanced cases

For most modern projects, Shade is the preferred choice because it handles class/resource merging more reliably and supports package relocation when dependency conflicts happen.

Mental Model

Think of your application like a lunchbox.

  • Your project's compiled code is the main meal.
  • Each dependency is a separate side dish in its own container.
  • A normal Maven JAR gives you only the main meal.
  • A runnable fat JAR packs everything into one box so you can carry and use it anywhere.

Without bundling dependencies, running the app is like showing up with only half the meal. The program starts, looks for a library, and cannot find it.

The Maven plugin's job is to collect all the pieces, put them together, and label the box with instructions saying, "Start from this main class."

Take Quiz

Syntax and Examples

Using Maven Shade Plugin

This is the most common way to build a single executable JAR with dependencies.

Add this to your pom.xml:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-shade-plugin</artifactId>
      <version>3.5.1</version>
      <executions>
        <execution>
          <phase>package</phase>
          <goals>
            <goal>shade</goal>
          </goals>
          <configuration>
            <createDependencyReducedPom>false</createDependencyReducedPom>
            <transformers>
              <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                <mainClass>com.example.Main</mainClass>
              </transformer>
            </transformers>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Then build the project:

mvn clean package

This typically creates a JAR in target/ that includes your dependencies.

Example Java main class

package com.example;

public class Main {
    public static void main(String[] args) {
        System.out.println("Application started");
    }
}

With the plugin configured correctly, you can run:

java -jar target/your-artifact-name.jar

Using Maven Assembly Plugin

Another option is the Assembly Plugin:

<build>
  <plugins>
    <plugin>
      <artifactId>maven-assembly-plugin</artifactId>
      <version>3.6.0</version>
      <configuration>
        <archive>
          <manifest>
            <mainClass>com.example.Main</mainClass>
          </manifest>
        </archive>
        <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
      </configuration>
      <executions>
        <execution>
          <id>make-assembly</id>
          <phase>package</phase>
          <goals>
            <goal>single</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

This often produces a file such as:

target/myapp-1.0-SNAPSHOT-jar-with-dependencies.jar

Both plugins can work, but Shade is usually better for production applications.

Step by Step Execution

Consider this setup:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.5.1</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <transformers>
          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>com.example.Main</mainClass>
          </transformer>
        </transformers>
      </configuration>
    </execution>
  </executions>
</plugin>

Now walk through what happens when you run:

mvn package

1. Maven compiles your code

Maven compiles files in src/main/java into .class files.

2. Maven resolves dependencies

It downloads any libraries listed in your pom.xml if they are not already available locally.

3. The package phase starts

Normally, Maven would create a regular JAR with only your code.

4. Shade plugin runs during package

Because the plugin execution is bound to the package phase, Maven calls the shade goal.

5. Dependency contents are merged

The plugin unpacks dependency JARs and includes their classes/resources in the final JAR.

6. The manifest is updated

The ManifestResourceTransformer writes the Main-Class entry so Java knows where execution should start.

7. The final JAR is written to target/

You get a runnable JAR that can usually be started with:

java -jar target/your-app.jar

8. Java reads the manifest and starts main

When the JAR is executed, the JVM reads the manifest, finds com.example.Main, and calls:

public static void main(String[] args)

That is what makes the JAR executable.

Real World Use Cases

Creating an executable JAR with dependencies is common in many real applications:

  • Command-line tools: internal utilities, data importers, report generators
  • Batch jobs: nightly processing tasks, ETL jobs, scheduled cleanup jobs
  • Small services: standalone Java apps launched by scripts or containers
  • Desktop launchers: Java-based tools distributed to users
  • CI/CD automation tools: build helpers, release scripts, code analysis tools

Example scenarios:

  • A team distributes a log-processing tool as one JAR so other teams can run it without manual classpath setup.
  • A data migration app uses JDBC and JSON libraries and needs to run as a single deployable file.
  • A scheduled background job is copied to a server and started with java -jar from cron or systemd.

In all of these cases, bundling dependencies reduces setup mistakes and makes deployment simpler.

Take Quiz

Real Codebase Usage

In real projects, developers usually combine executable JAR packaging with a few common patterns:

Guarding the entry point

The main method often validates required arguments early:

public static void main(String[] args) {
    if (args.length == 0) {
        System.out.println("Usage: java -jar app.jar <input-file>");
        return;
    }

    System.out.println("Processing: " + args[0]);
}

This is an example of an early return or guard clause.

Keeping packaging logic in the build, not the code

Developers usually avoid writing custom scripts to copy JARs around manually. Instead, they configure the build in pom.xml so anyone can run the same command:

mvn clean package

Handling resources and manifests

Applications often need:

  • a correct Main-Class
  • merged service loader files
  • merged framework metadata

This is one reason Shade is popular: it supports resource transformers for these cases.

Using profiles for different packaging needs

Some teams create one artifact for local development and another shaded artifact for distribution.

Error handling in startup code

Real applications often fail fast if configuration is missing:

public static void main(String[] args) {
    String env = System.getenv("APP_ENV");
    if (env == null) {
        throw new IllegalStateException("APP_ENV is required");
    }

    System.out.println("Starting in environment: " + env);
}

This keeps runtime failures clearer after packaging.

Common Mistakes

1. Forgetting to set the main class

If the manifest does not contain the main class, this command will fail:

java -jar myapp.jar

Typical error:

no main manifest attribute

Broken example

<transformers>
</transformers>

Fix

Add a manifest transformer or manifest configuration with the correct fully qualified class name.

2. Using the wrong main class name

The value must include the package.

Broken example

<mainClass>Main</mainClass>

Correct

<mainClass>com.example.Main</mainClass>

3. Expecting the default Maven JAR to include dependencies

This does not happen automatically with the standard maven-jar-plugin.

Broken assumption

mvn package
java -jar target/app.jar

If dependencies are external, the app may crash with missing class errors.

4. Confusing Assembly and Shade

Assembly can work for simple cases, but Shade is often more reliable when dependencies contain overlapping resources.

5. Not checking the output file name

Plugins may generate a different artifact name than expected, such as:

app-1.0-SNAPSHOT-shaded.jar
app-1.0-SNAPSHOT-jar-with-dependencies.jar

Always inspect the target/ directory.

6. Ignoring resource conflicts

Some libraries contain files like:

META-INF/services/...

If these are not merged correctly, runtime behavior may break even though the build succeeds.

7. Bundling dependencies with incompatible scopes

Dependencies with provided scope are usually not packaged because the runtime environment is expected to provide them.

Example:

<dependency>
  <groupId>jakarta.servlet</groupId>
  <artifactId>jakarta.servlet-api</artifactId>
  <version>6.0.0</version>
  <scope>provided</scope>
</dependency>

That is correct for some server deployments, but not for a standalone executable JAR.

Comparisons

ApproachWhat it doesBest forNotes
Default Maven JARPackages only your project classes/resourcesLibraries and simple artifactsNot enough for standalone apps with dependencies
Maven Assembly PluginBuilds a JAR with dependencies using predefined descriptorsSimple packaging setupsEasier to start with, less flexible for advanced merging
Maven Shade PluginBuilds a merged executable JARMost standalone Java appsBest choice for fat JARs and dependency/resource handling

Shade vs Assembly

FeatureShade PluginAssembly Plugin
Creates one runnable JARYesYes
Handles merged resources betterYesLimited compared to Shade
Supports relocation of packagesYesNo practical equivalent for most cases
Good for production appsUsually yesSometimes
Simpler for basic useModerateOften simpler

Fat JAR vs regular JAR

TypeContains dependencies?Runnable alone?
Regular JARNoUsually no
Fat/Uber JARYesUsually yes
Take Quiz

Cheat Sheet

Maven executable JAR quick reference

Default behavior

mvn package
  • Creates a normal JAR
  • Usually does not include dependencies

Shade plugin essentials

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.5.1</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <transformers>
          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>com.example.Main</mainClass>
          </transformer>
        </transformers>
      </configuration>
    </execution>
  </executions>
</plugin>

Assembly plugin essentials

<plugin>
  <artifactId>maven-assembly-plugin</artifactId>
  <version>3.6.0</version>
  <configuration>
    <archive>
      <manifest>
        <mainClass>com.example.Main</mainClass>
      </manifest>
    </archive>
    <descriptorRefs>
      <descriptorRef>jar-with-dependencies</descriptorRef>
    </descriptorRefs>
  </configuration>
</plugin>

Run the app

java -jar target/app.jar

Common errors

  • no main manifest attribute → missing Main-Class
  • ClassNotFoundException → dependency not packaged
  • wrong output file name → check target/

Rules to remember

  • Use a fully qualified main class name
  • Prefer Shade for most standalone apps
  • provided dependencies are not typically bundled
  • Always test the generated JAR with java -jar

FAQ

How do I make a JAR executable in Maven?

Add a plugin such as maven-shade-plugin or maven-assembly-plugin and set the Main-Class in the manifest.

Does mvn package include dependencies by default?

No. The default JAR usually contains only your project's classes and resources.

What is the difference between a fat JAR and a normal JAR?

A normal JAR contains only your app code. A fat JAR also contains dependency classes so it can usually run by itself.

Which Maven plugin should I use for a runnable JAR with dependencies?

For most cases, use maven-shade-plugin. It is the most common option for building a single executable JAR.

Why do I get no main manifest attribute?

Your JAR manifest does not define the Main-Class entry, or it points to the wrong class.

Why do I get ClassNotFoundException after packaging?

Your dependencies were not included, or the wrong plugin/configuration was used.

Can I use provided dependencies in a standalone JAR?

Usually no. provided means the runtime environment is expected to supply them.

Where is the generated JAR stored?

Usually in the target/ directory after running mvn package.

Take Quiz

Mini Project

Description

Build a small command-line Java application and package it as a single runnable JAR using Maven Shade Plugin. This project demonstrates the exact workflow used in many real applications: write a main class, add a dependency, configure Maven to bundle everything, and run the output with java -jar.

Goal

Create a Maven project that prints a message using a third-party library and package it into one executable JAR with all dependencies included.

Requirements

  1. Create a Maven Java project with a Main class.
  2. Add at least one external dependency to the project.
  3. Configure Maven Shade Plugin to set the main class and bundle dependencies.
  4. Build the project with mvn clean package.
  5. Run the generated JAR with java -jar.
Take Quiz

Keep learning

Related questions

View more