Package clojure projects
Switch branches/tags
Nothing to show
Clone or download


Pack (alpha)

Package up Clojure projects in various ways, based on a deps.edn file.


Auto-add to project

Pack can add itself to your deps.edn automatically. It will not modify your indentation style at all. This is alpha, but so is everything else in this project.

It can be run at later times, in order to automatically bump the sha in your deps.edn. It will continue to work even if you rename the default :pack alias to something else.

This isn’t infallible. Please let me know if I break your indentation.
clojure -Sdeps '{:deps {pack/pack.alpha {:git/url "" :sha "dccf2134bcf03726a9465d2b9997c42e5cd91bff"}}}' -m mach.pack.alpha.inject 'd9023b24c3d589ba6ebc66c5a25c0826ed28ead5'

The following examples assume you have run this.


The lambda approach will generate a zip which can be uploaded to AWS.

clj -A:pack deps.edn build_dir

In this example:

  • deps.edn is the location of the deps.edn to consume.

  • is where to write the package to.

  • build_dir is a directory containing extra paths to add, for example it may be where you compile classes to, or write built css to.


"Uberjar-ing" consists of packaging you application into one all-encompassing JAR (Java ARchive) file, called an "Uberjar", which can then be executed using java -jar myapp.jar ARG1 ARG2 ARG3…​.

Pack provides several approaches for creating an Uberjar:


This consists of building an Uberjar using Parallel Universe’s Capsule.

$ clj -A:pack mach.pack.alpha.capsule uberjar.jar -e build-dir --application-id mycoolapp --application-version "$(git describe)" -m myapp.main
$ java -jar uberjar.jar
Usage: clj -m mach.pack.alpha.capsule [options] <path/to/output.jar>
  -m, --main SYMBOL                           main namespace
      --application-id STRING                 globally unique name for application, used for caching
      --application-version STRING            unique version for this uberjar, used for caching
      --system-properties STRING              space-separated list of propName=value pairs, specifying JVM System Properties which will be passed to the application. Maps to the 'System-Properties' entry in the Capsule Manifest.
      --jvm-args STRING                       space-separated list of JVM argument that will be used to launch the application (e.g "-server -Xms200m -Xmx600m"). Maps to the 'JVM-Args' entry in the Capsule Manifest.
  -e, --extra-path STRING                     add directory to classpath for building
  -d, --deps STRING                 deps.edn  deps.edn file location
  -M, --manifest-entry STRING                 a "Key: Value" pair that will be appended to the Capsule Manifest; useful for conveying arbitrary Manifest entries to the Capsule Manifest. Can be repeated to supply several entries.
  -h, --help                                  show this help

The main does not need :gen-class, it follows the same semantics as clojure.main’s -m main. If main is not specified, it will default to clojure.main, meaning it can take options like -m or -r. extra-path is useful for adding clojurescript & css to the resulting jar. It can be used multiple times.

Executing a Capsule JAR with JVM args / System Properties

As mentioned in the Capsule docs, when you execute a Capsule-packaged Uberjar, 2 Java processes are actually run: the Capsule launcher, and your application process.

In particular, JVM args and System Properties passed to the java command will apply to both processes, which if you’re not careful can result in waste of resources and collisions; for example:

  • If you pass the -Xms2g JVM arg, you’ve just allocated 2 Gigabytes of unnecessary RAM for the Capsule launcher

  • If you pass the System Property you will get an error, as both Java processes try to use the 7091 port for JMX.

The way to prevent this is to pass special options that will only apply to the application process. See the related Capsule documentation.

INFO: In the future I will extend Capsule support to include capsule-specific features, such as caplets and many of the related manifest attributes.


OneJAR builder. This is a suitable replacement for jdsoft JCL, without the GPL license. It’s especially practical where you can’t extract into a ~/.capsule directory, or don’t need to bake in JVM args.

Usage: clj -m [options] <path/to/output.jar>

  -e, --extra-path STRING            add directory to classpath for building
  -d, --deps STRING        deps.edn  deps.edn file location
  -h, --help                         show this help

output.jar is where to put the output uberjar. Leading directories will be created.


$ clj -A:pack output.jar
$ java -jar output.jar

jdsoft JarClassLoader

This has been removed due to my own licensing concerns about co-locating the source with my own.

Skinny JAR

$ clj -A:pack mach.pack.alpha.skinny
$ java -cp "target/app.jar:target/lib/*" clojure.main

Uploading to Clojars (or Maven)

Using skinny jars, pack can be used to upload artifacts to Clojars. This example should be easily modified to work with Maven repositories too, but I haven’t tried. A full example can be found at super-duper-octo-barnacle.

  1. Follow Clojars maven guide for "settings.xml" only

  2. You can generate your jar by passing the --no-libs option to your jar. Also in this example is --project-path, although this is not strictly required. I highly recommend putting this in a Makefile or shell script.

    clojure -Sdeps '{:deps {pack/pack.alpha {:git/url "" :sha "8acf80dd4d6e5173585f5c6fec7af28a310f3ed7"}}}' -m mach.pack.alpha.skinny --no-libs --project-path my-cool-lib.jar
  3. You will also need a pom.xml, you can generate one with clojure:

    clojure -Spom
  4. Update the pom.xml as necessary to correct your groupId or version. Future calls to pom.xml won’t change them back.

  5. Use mvn command line to deploy

    mvn deploy:deploy-file -Dfile=my-cool-lib.jar -DrepositoryId=clojars -Durl= -DpomFile=pom.xml