1. Introduction

Shadow is a Gradle plugin for combining dependency classes and resources with a project’s into a single output Jar. The combined Jar is often referred to a fat-jar or uber-jar. Shadow utilizes JarInputStream and JarOutputStream to efficiently process dependent libraries into the output jar without incurring the I/O overhead of expanding the jars to disk.

1.1. Benefits of Shadow

Shadowing a project output has 2 major use cases:

  1. Creating an executable JAR distribution

  2. Bundling and relocating common dependencies in libraries to avoid classpath conflicts

1.1.1. Executable Distributions

Executable distribution is the main use case for application developers. The goal of an executable distribution is a single file that can be deployed and executed/run in the runtime environment. In the case of Shadow, this is a single uber or fat JAR. The JAR file contains all the application code and dependent libraries to execute (not including the standard JVM libraries). The shadow JAR does not include the JRE itself. It must be available on the target system.

Executable JARs contain a JAR MANIFEST that specifies the application Main Class. This allows the application to be started with a single command:

$ java -jar application-shadow.jar

1.1.2. Library Bundling

Dependency bundling and relocation is the main use case for library authors. The goal of a bundled library is to create a pre-packaged dependency for other libraries or applications to utilize. Often in these scenarios, a library may contain a dependency that a downstream library or application also uses. In some cases, different versions of this common dependency can cause an issue in either the upstream library or the downstream application. These issues often manifest themselves as binary incompatibilities in either the library or application code.

By utilizing Shadow’s ability to relocate the package names for dependencies, a library author can ensure that the library’s dependencies will not conflict with the same dependency being declared by the downstream application.

1.2. Getting Started

buildscript {
    repositories {
    dependencies {
        classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.4'

apply plugin: 'com.github.johnrengelman.shadow'

Alternatively, the Gradle Plugin syntax can be used:

plugins {
  id 'com.github.johnrengelman.shadow' version '1.2.4'

Shadow is a reactive plugin. This means that applying Shadow by itself will perform no configuration on your project. Instead, Shadow reacts to the application of other plugins to decorate the project.

This means, that for most users, the java or groovy plugins must be explicitly applied to have the desired effect.

1.3. Default Java/Groovy Tasks

In the presence of the java or groovy plugins, Shadow will automatically configure the following behavior:

  • Adds a shadowJar task to the project.

  • Adds a shadow configuration to the project.

  • Configures the shadowJar task to include all sources from the project’s main sourceSet.

  • Configures the shadowJar task to bundle all dependencies from the runtime configuration.

  • Configures the classifier attribute of the shadowJar task to be 'all' .

  • Configures the shadowJar task to generate a Manifest with:

    • Inheriting all configuration from the standard jar task.

    • Adds a Class-Path attribute to the Manifest that appends all dependencies from the shadow configuration

  • Configures the shadowJar task to exclude any JAR index or cryptographic signature files matching the following patterns:


    • META-INF/*.SF

    • META-INF/*.DSA

    • META-INF/*.RSA

  • Creates and registers the shadow component in the project (used for integrating with maven-publish).

  • Configures the uploadShadow task (as part of the maven plugin) with the following behavior:

    • Removes the compile and runtime configurations from the pom.xml file mapping.

    • Adds the shadow configuration to the pom.xml file as RUNTIME scope.

2. Configuring Shadow

The ShadowJar task type extends from Gradle’s Jar type. This means that all attributes and methods available on Jar are also available on ShadowJar. Refer the Gradle User Guide for Jar for details.

2.1. Configuring Output Name

Shadow configures the default shadowJar task to set the output JAR’s destinationDir, baseName, appendix, version, and extension to the same default values as Gradle does for all Jar tasks. Additionally, it configures the classifier to be all.

If working with a Gradle project with the name myApp and version 1.0, the default shadowJar task will output a file at: build/libs/myApp-1.0-all.jar

As with all Jar tasks in Gradle, these values can be overridden:

Output to build/libs/shadow.jar
shadowJar {
   baseName = 'shadow'
   classifier = null
   version = null

2.2. Configuring the Runtime Classpath

Each Java JAR file contains a manifest file that provides meta data about the contents of the JAR file itself. When using a shadowed JAR file as an executable JAR, it is assumed that all necessary runtime classes are contained within the JAR itself. There may be situations where the desire is to not bundle select dependencies into the shadowed JAR file but they are still required for runtime execution.

In these scenarios, Shadow creates a shadow configuration to declare these dependencies. Dependencies added to the shadow configuration are not bundled into the output JAR.

Additionally, Shadow automatically configures the manifest of the shadowJar task to contain a Class-Path entry in the JAR manifest. The value of the Class-Path entry is the name of all dependencies resolved in the shadow configuration for the project.

dependencies {
  shadow 'junit:junit:3.8.2'

Inspecting the META-INF/MANIFEST.MF entry in the JAR file will reveal the following attribute:

Class-Path: junit-3.8.2.jar

When deploying a shadowed JAR as an execution JAR, it is important to note that any non-bundled runtime dependencies must be deployed in the location specified in the Class-Path entry in the manifest.

2.3. Configuring the JAR Manifest

Beyond the autmatic configuration of the Class-Path entry, the shadowJar manifest is configured in a number of ways. First, the manifest for the shadowJar task is configured to inherit from the manifest of the standard jar task. This means that any configuration performed on the jar task will propagate to the shadowJar tasks.

jar {
   manifest {
       attributes 'Class-Path': '/libs/a.jar'

Inspecting the META-INF/MANIFEST.MF entry in the JAR file will revel the following attribute:

Class-Path: /libs/a.jar

If it is desired to inherit a manifest from a JAR task other than the standard jar task, the inheritFrom methods on the shadowJar.manifest object can be used to configure the upstream.

task testJar(type: Jar) {
  manifest {
    attributes 'Description': 'This is an application JAR'

shadowJar {
  manifest {
    inheritFrom project.tasks.testJar.manifest

2.4. Filtering Shadow Jar Contents

The final contents of a shadow JAR can be filtered using the exclude and include methods inherited from Gradle’s Jar task type.

Refer to the Jar documentation for details on the various versions of the methods and their behavior.

When using exclude/include with a ShadowJar task, the resulting copy specs are applied to the final JAR contents. This means that, the configuration is applied to the individual files from both the project source set or any of the dependencies to be merged.

Exclude a file from Shadow Jar
shadowJar {
   exclude 'a2.properties'

Excludes and includes can be combined just like a noraml Jar task, with excludes taking precendence over includes. Additionally, ANT style patterns can be used to match multiple files.

Configuring output using ANT patterns
shadowJar {
   include '*.jar'
   include '*.properties'
   exclude 'a2.properties'

2.5. Configuring Shadowed Dependencies

Shadow configures the default shadowJar task to merge all dependencies from the project’s runtime configuration into the final JAR. The configurations to source dependencies from for the merging can be configured using the configurations property of the ShadowJar task type.

shadowJar {
  configurations = [project.configurations.compile]

The above code sample would configure the shadowJar task to merge depdencies from only the compile configuration. This means any dependency declared in the runtime configuration would be not be included in the final JAR.

Note the literal use of project.configurations when setting the configurations attribute of a ShadowJar task. This is required. It maybe be tempting to specify configurations = [configurations.compile] but this will not have the intended effect, as configurations.compile will try to delegate to the configurations property of the the ShadowJar task instead of the project

2.6. Filtering Dependencies

Individual dependencies can be filtered from the final JAR by using the dependencies block of a ShadowJar task. Dependency filtering does not apply to transitive dependencies. That is, excluding a dependency does not exclude any of its dependencies from the final JAR.

The dependency blocks provides a number of methods for resolving dependencies using the notations familiar from Gradle’s configurations block.

Exclude an Module Dependency
dependencies {
   compile 'shadow:d:1.0'

shadowJar {
   dependencies {
Exclude a Project Dependency
dependencies {
  compile project(':client')

shadowJar {
   dependencies {

While not being able to filter entire transitive dependency graphs might seem like an oversight, it is necessary because it would not be possible to intelligently determine the build author’s intended results when there is a common dependency between two 1st level dependencies when one is excluded and the other is not.

2.6.1. Using Regex Patterns to Filter Dependencies

Dependencies can be filtered using regex patterns. Coupled with the <group>:<artifact>:<version> notation for dependencies, this allows for excluding/including using any of these individual fields.

Exclude Any Version of a Dependency
dependencies {
   compile 'shadow:d:1.0'

shadowJar {
   dependencies {

Any of the individual fields can be safely absent and will function as though a wildcard was specified.

Ignore Dependency Version
shadowJar {
  dependencies {

The above code snippet is functionally equivalent to the previous example.

This same patten can be used for any of the dependency notation fields.

Ignoring An Artifact Regardless of Group
shadowJar {
  dependencies {
Excluding All Artifacts From Group
shadowJar {
  dependencies {

2.6.2. Programmatically Selecting Dependencies to Filter

If more complex decisions are needed to select the dependencies to be included, the dependencies block provides a method that accepts a Closure for selecting dependencies.

Selecting Dependencies to Filter With a Spec
shadowJar {
   dependencies {
       exclude(dependency {
           it.moduleGroup == 'junit'

2.7. Controlling JAR Content Merging

Shadow allows for customizing the process by which the output JAR is generated through the Transformer interface. This is a concept that has been carried over from the original Maven Shade implementation. A Transformer is invoked for each entry in the JAR before being written to the final output JAR. This allows a Transformer to determine if it should process a particular entry and apply any modifications beforewriting the stream to the output.

Adding a Transformer
shadowJar {

Additionally, a Transformer can accept a Closure to configure the provided Transformer.

Configuring a Transformer
shadowJar {
  transform(MyTransformer.class) {
    enable = true

An instantiated instance of a Transformer can also be provided.

Adding a Transformer Instance
shadowJar {
  transform(new MyTransformer(enabled: true))

2.7.1. Merging Service Descriptor Files

Java libraries often contain service descriptors files in the META-INF/services directory of the JAR. A service descriptor typically contains a line delimited list of classes that are supported for a particular service. At runtime, this file is read and used to configure library or application behavior.

Multiple dependencies may use the same service descriptor file name. In this case, it is generally desired to merge the content of each instance of the file into a single output file. The ServiceFileTransformer class is used to perform this merging. By default, it will merge each copy of a file under META-INF/services into a single file in the output JAR.

Merging Service Files
shadowJar {

The above code snippet is a convenience syntax for calling transform(ServiceFileTransformer.class).

Groovy Extension Module descriptor files (located at META-INF/services/org.codehaus.groovy.runtime.ExtensionModule) are ignored by the ServiceFileTransformer. This is due to these files having a different syntax than standard service descriptor files. Use the mergeGroovyExtensionModules() method to merge these files if your dependencies contain them.

Configuring the Location of Service Descriptor Files

By default the ServiceFileTransformer is configured to merge files in META-INF/services. This directory can be overridden to merge descriptor files in a different location.

Merging Service Files in a Specific Directory
shadowJar {
  mergeServiceFiles {
    path = 'META-INF/custom'
Excluding/Including Specific Service Descriptor Files From Merging

The ServiceFileTransformer class supports specifying specific files to include or exclude from merging.

Excluding a Service Descriptor From Merging
shadowJar {
  mergeServiceFiles {
    exclude 'META-INF/services/com.acme.*'

2.7.2. Merging Groovy Extension Modules

Shadow provides a specific transformer for dealing with Groovy extension module files. This is due to their special syntax and how they need to be merged together. The GroovyExtensionModuleTransformer will handle these files. The ShadowJar task also provides a short syntax method to add this transformer.

Merging Groovy Extension Modules
shadowJar {

2.7.3. Appending Text Files

Generic text files can be appended together using the AppendingTransformer. Each file is appended using new lines to separate content. The ShadowJar task provides a short syntax method of append(String) to configure this transformer.

Appending a Property File
shadowJar {
  append 'test.properties'

2.7.4. Appending XML Files

XML files require a special transformer for merging. The XmlAppendingTransformer reads each XML document and merges each root element into a single document. There is no short syntax method for the XmlAppendingTransformer. It must be added using the transform methods.

Appending a XML File
shadowJar {
  tranform(XmlAppendingTransformer.class) {
    resource = 'properties.xml'

2.8. Relocating Packages

Shade is capable of scanning a project’s classes and relocating specific dependencies to a new location. This is often required when one of the dependencies is susceptible to breaking changes in versions or to classpath pollution in a downstream project.

Google’s Guava and the ASM library are typical cases where package relocation can come in handy.

Shadow uses the ASM library to modify class byte code to replace the package name and any import statements for a class. Any non-class files that are stored within a package structure are also relocated to the new location.

Relocating a Package
shadowJar {
   relocate 'junit.framework', 'shadow.junit'

The code snippet will rewrite the location for any class in the junit.framework to be shadow.junit. For example, the class junit.textui.TestRunner becomes shadow.junit.TestRunner. In the resulting JAR, the class file is relocated from junit/textui/TestRunner.class to shadow/junit/TestRunner.class.

Relocation operates at a package level. It is not necessary to specify any patterns for matching, it will operate simply on the prefix provided.

Relocation will be applied globally to all instance of the matched prefix. That is, it does not scope to only the dependencies being shadowed. Be specific as possible when configuring relocation as to avoid unintended relocations.

2.8.1. Filtering Relocation

Specific classes or files can be included/excluded from the relocation operation if necessary.

Configuring Filtering for Relocation
shadowJar {
   relocate('junit.textui', 'a') {
       exclude 'junit.textui.TestRunner'
   relocate('junit.framework', 'b') {
       include 'junit.framework.Test*'

3. Creating a Custom ShadowJar Task

The built in shadowJar task only provides an output for the main source set of the project. It is possible to add arbitrary ShadowJar tasks to a project. When doing so, ensure that the configurations property is specified to inform Shadow which dependencies to merge into the output.

Shadowing Test Sources and Dependencies
task testJar(type: ShadowJar) {
  classifier = 'tests'
  from sourceSets.test.output
  configurations = [project.configurations.testRuntime]

The code snippet above will geneated a shadowed JAR contain both the main and test sources as well as all runtime and testRuntime dependencies. The file is output to build/libs/<project>-<version>-tests.jar.

4. Integrating with Application Plugin

Shadow reacts to the presence of Gradle’s application plugin and will automatically configure additional tasks for running the shadowed JAR and creating distributions containing the shadowed JAR.

Just like the normal jar task, when the application plugin is applied, the shadowJar manifest will be configured to contain the Main-Class attribute with the value specified in the project’s mainClassName atrribute.

Using Shadow with Application Plugin
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'

mainClassName = 'myapp.Main'

4.1. Running the Shadow JAR

When applied along with the application plugin, the runShadow task will be created for starting the application from the shadowed JAR. The runShadow task is a JavaExec task that is configured to execute java -jar myproject-all.jar. It can be configured the same as any other JavaExec task.

Configuring the runShadow Task
runShadow {
  args 'foo'

4.2. Distributing the Shadow JAR

The Shadow plugin will also configure distribution tasks when in the presence of the application plugin. The plugin will create distShadowZip and distShadowTar which creates Zip and Tar distributions respectively. Each distribution will contain the shadowed JAR file along with the necessary start scripts to launch the application.

Additionally, the plugin will create the installShadowApp and startShadowScripts tasks which stages the necessary files for a distribution to build/installShadow.

5. Publishing Shadow JARs

5.1. Publishing with Maven-Publish Plugin

The Shadow plugin will automatically configure the necessary tasks in the presence of Gradle’s maven-publish plugin. THe plugin will create and configure a software component named "shadow" for use with the publishing block.

Publishing a Shadow JAR with the Maven-Publish Plugin
apply plugin: 'java'
apply plugin: 'maven-publish'
apply plugin: 'com.github.johnrengelman.shadow'

publishing {
  publications {
    shadow(MavenPublication) {
      from components.shadow
  repositories {
    maven {
      url "http://repo.myorg.com"

5.2. Publishing with Maven Plugin

The Shadow plugin will automatically configure the necessary tasks in the presence of Gradle’s maven plugin. To publish the JAR, simply configure the publish location for the uploadShadow task and execute it.

Publishing a Shadow JAR with the Maven Plugin
apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'com.github.johnrengelman.shadow'

uploadShadow {
  repositories {
    mavenDeployer {
      repository(url: "http://repo.myorg.com")

6. Using Shadow in Multi-Project Builds

When using Shadow in a multi-project build, project dependencies will be treated the same as external dependencies. That is a project dependency will be merged into the shadowJar output of the project that is applying the Shadow plugin.

6.1. Depending on the Shadow Jar from Another Project

In a multi-project build there may be one project that applies Shadow and another that requires the shadowed JAR as a dependency. In this case, use Gradle’s normal dependency declaration mechanism to depend on the shadow configuration of the shadowed project.

Depending On Shadow Output of Project
dependencies {
  compile project(path: 'api', configuration: 'shadow')

7. Change Log

v1.3.0 (Unreleased)

  • Build Shadow w/ Shadow. This will help prevent any future classpath conflicts with Gradle.





  • Apply package relocations to dependency resources (Issue #114)


  • Re-organize some code to remove need for forcing the Gradle API ClassLoader to allow the org.apache.tools.zip package.

  • Upgrade JDOM library from 1.1 to 2.0.5 (change dependency from jdom:jdom:1.1 to org.jdom:jdom2:2.0.5) (Issue #98)

  • Convert ShadowJar.groovy to ShadowJar.java to workaround binary incompatibility introduced by Gradle 2.2 ((Issue #106)

  • Updated ASM library to 5.0.3 to support JDK8 (Issue #97)

  • Allows for regex pattern matching in the dependency string when including/excluding (Issue #83)

  • Apply package relocations to resource files (Issue #93)


  • fix bug in runShadow where dependencies from the shadow configuration are not available (Issue #94)


  • Fix bug in 'createStartScripts' task that was causing it to not execute 'shadowJar' task (Issue #90)

  • Do not include null in ShadowJar Manifest 'Class-Path' value when jar task does not specify a value for it. (Issue #92)

  • ShadowJar Manifest 'Class-Path' should reference jars from 'shadow' config as relative to location of shadowJar output (Issue #91)


  • Breaking Change! Fix leaking of shadowJar.manifest into jar.manifest. (Issue #82) To simplify behavior, the shadowJar.appendManifest method has been removed. Replace uses with shadowJar.manifest

  • ShadowTask now has a configurations property that is resolved to the files in the resolved configuration before being added to the copy spec. This allows for an easier implementation for filtering. The default 'shadowJar' task has the convention of adding the 'runtime' scope to this list. Manually created instances of ShadowTask have no configurations added by default and can be configured by setting task.configurations.

  • Properly configure integration with the 'maven' plugin when added. When adding 'maven' the 'uploadShadow' task will now properly configure the POM dependencies by removing the 'compile' and 'runtime' configurations from the POM and adding the 'shadow' configuration as a RUNTIME scope in the POM. This behavior matches the behavior when using the 'maven-publish' plugin.

  • Matt Hurne - Allow ServiceFileTransformer to specify include/exclude patterns for files within the configured path to merge.

  • Matt Hurne - Added GroovyExtensionModuleTransformer for merging Groovy Extension module descriptor files. The existing ServiceFileTransformer now excludes Groovy Extension Module descriptors by default.

  • distShadowZip and distShadowZip now contain the shadow library and run scripts instead of the default from the 'application' plugin (Issue #89)


  • Make service files root path configurable for ServiceFileTransformer (Issue #72)

  • Andres Almiray - Added PropertiesFileTransformer (Issue #73)

  • Brandon Kearby - Fixed StackOverflow when a cycle occurs in the resolved dependency graph (Issue #69)

  • Apply Transformers to project resources (Issue #70), Issue #71)

  • Do not drop non-class files from dependencies when relocation is enabled. Thanks to Minecrell for digging into this. (Issue #61)

  • Remove support for applying individual sub-plugins by Id (easier maintenance and cleaner presentation in Gradle Portal)


  • Do not add an empty Class-Path attribute to the manifest when the shadow configuration contains no dependencies.

  • runShadow now registers shadowJar as an input. Previously, runShadow did not execute shadowJar and an error occurred.

  • Support Gradle 2.0 (Issue #66)

  • Do not override existing 'Class-Path' Manifest attribute settings from Jar configuration. Instead combine. (Issue #65)


  • Fix issue where non-class files are dropped when using relocation (Issue #58)

  • Do not create a / directory inside the output jar.

  • Fix runShadow task to evaluate the shadowJar.archiveFile property at execution time. (Issue #60)


  • Previously known as v0.9.0

  • All changes from 0.9.0-M1 to 0.9.0-M5

  • Properly configure the ShadowJar task inputs to observe the include/excludes from the dependencies block. This allows UP-TO-DATE checking to work properly when changing the dependencies rules (Issue #54)

  • Apply relocation remappings to classes and imports in source project (Issue #55)

  • Do not create directories in jar for source of remapped class, created directories in jar for destination of remapped classes (Issue #53)


  • Add commons-io to compile classpath

  • Update asm library to 4.1


  • Break plugin into multiple sub-plugins. ShadowBasePlugin is always applied. ShadowJavaPlugin and ShadowApplicationPlugin are applied in reaction to applying the java and application plugins respectively.

  • Shadow does not applied java plugin automatically. java or groovy must be applied in conjunction with shadow.

  • Moved artifact filtering to dependencies {} block underneath shadowJar. This allows better include/exclude control for dependencies.

  • Dependencies added to the shadow configuration are automatically added to the Class-Path attribute in the manifest for shadowJar

  • Applying application plugin and settings mainClassName automatically configures the Main-Class attribute in the manifest for shadowJar

  • runShadow now utilizes the output of the shadowJar and executes using java -jar <shadow jar file>

  • Start Scripts for shadow distribution now utilize java -jar to execute instead of placing all files on classpath and executing main class.

  • Excluding/Including dependencies no longer includes transitive dependencies. All dependencies for inclusion/exclusion must be explicitly configured via a spec.


  • Use commons.io FilenameUtils to determine name of resolved jars for including/excluding


  • Added integration with application plugin to replace old OutputSignedJars task

  • Fixed bug that resulted in duplicate file entries in the resulting Jar

  • Changed plugin id to 'com.github.johnrengelman.shadow' to support Gradle 2.x plugin infrastructure.


  • Rewrite based on Gradle Jar Task

  • ShadowJar now extends Jar

  • Removed signedCompile and signedRuntime configurations in favor of shadow configuration

  • Removed OutputSignedJars task

8. About This Project

I started this project in December of 2012. We were working on converting from a monolithic application into a the new hot jazz of "microservices" using Dropwizard. I had also just started learning about Gradle and I knew that the incremental build system it provided would benefit our development team greatly. Unfortunately, the closest thing that Gradle had to Maven’s Shade plugin was its ability to create application TARs and ZIPs.

So, Charlie Knudsen and myself set out to port the existing Shade code into a Gradle plugin. This port is what existing up until the 0.9 milestone releases for Shadow. It functioned, but it wasn’t idiomatic Gradle by any means.

Starting with 0.9, Shadow was rewritten from the ground up as standard Gradle plugin and leveraged as much of Gradle’s classes and concepts as possible. At the same time as the 0.9 release, Gradle was announcing the Gradle Plugin Portal and so Shadow was published there.

Shadow has had nearly ~900,000 downloads from Bintray and countless more from the Gradle Plugin Portal.

8.1. Maintainers