by taylorwood

taylorwood / lein-native-image

A Leiningen plugin to build GraalVM native images

209 Stars 4 Forks Last release: about 1 year ago (v0.3.1) MIT License 29 Commits 4 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:


A Leiningen plugin for generating GraalVM native images from your project.


lein native-image
command compiles your project then uses GraalVM's
to build a native image.

Clojars Project

For deps.edn projects, try clj.native-image.


  • This plugin depends on GraalVM to build native images.

NOTE: As of GraalVM 19.0.0,

is no longer included by default:

Native Image was extracted from the base GraalVM distribution. Currently it is available as an early adopter plugin. To install it, run:

gu install native-image
. After this additional step, the
executable will be in the
directory, as for the previous releases.
  ➜ $GRAALVM_HOME/bin/gu install native-image
  Downloading: Component catalog from www.graalvm.org
  Processing component archive: Native Image
  Downloading: Component native-image: Native Image  from github.com
  Installing new component: Native Image licence files (org.graalvm.native-image, version 19.0.0)
  • Your project.clj must set a
    namespace w/entrypoint and support AOT compilation:
    :main ^:skip-aot my-app.core


See the examples directory for projects that can be compiled to native images with GraalVM:

  • jdnsmith - CLI command to read JSON from stdin and write EDN to stdout.
  • http-api - Basic HTTP server using Ring, Compojure, http-kit.
  • nlp - CLI command to analyze sentiment of text using StanfordNLP. Includes examples of reflection hints and delaying class initialization.
  • clojurl - cURL-like tool using clojure.spec, HTTPS, hiccup.


  1. Configure your project with a custom image name, path to GraalVM's home directory or

    path, or
    CLI options: ```clojure (defproject my-app "0.1.0" :plugins [[io.taylorwood/lein-native-image "0.3.1"]] ;; or in ~/.lein/profiles.clj

    :native-image {:name "my-app" ;; name of output image, optional :graal-bin "/path/to/graalvm/" ;; path to GraalVM home, optional :opts ["--verbose"]} ;; pass-thru args to GraalVM native-image, optional

    ;; optionally set profile-specific :native-image overrides :profiles {:test ;; e.g. lein with-profile +test native-image {:native-image {:opts ["--report-unsupported-elements-at-runtime" "--initialize-at-build-time" "--verbose"]}}

             :uberjar ;; used by default
             {:aot :all
              :native-image {:jvm-opts ["-Dclojure.compiler.direct-linking=true"]}}})
    `:native-image` config keys:
    • :name
      is an optional name for the generated native image.
    • The
      path can be specified as a string or resolved from an environment variable using a keyword e.g.
      . If
      is unspecified, the
      environment variable will be used by default.
    • :opts
      is an optional vector of arguments to
      ; see its documentation for more.

    Note: task-specific

    profile will be merged in by default, or the
    profile if that doesn't exist.

    You can also specify these in your Leiningen user profile

    {:user {:plugins [[io.taylorwood/lein-native-image "0.3.1"]]
            :native-image {:graal-bin "/path/to/graalvm-ce-19.0.0/Contents/Home/bin"}}}
  2. Build a native image from your project:

    ➜ lein native-image
    Compiling my-app.core
    Build on Server(pid: 36212, port: 26681)
       classlist:     332.89 ms
           (cap):   1,289.90 ms
  3. Execute the native image:

    ➜ ./target/my-app with optional args
    Hello, World!


The primary benefits to using a GraalVM native image are faster startup, lower memory requirements, and smaller distribution footprint (no JDK/JRE required). This doesn't necessarily mean the same code will run faster than it would on the JVM. GraalVM Community Edition and Enterprise Edition also have different performance characteristics.

GraalVM's native image capabilities have evolved across many release candidates. Several AOT issues have been fixed since 1.0.0-RC1. GraalVM and Substrate VM's support for AOT compilation and native images has limitations, and there are unsupported features. This release and its example projects were tested with GraalVM 19.0.0 Community Edition.

GraalVM 19.0.0 (first non-RC release) changes the default class-initialization behavior of

. Now you must specify
explicitly in your

There is a known issue where usages of

macro will fail compilation. Clojure 1.10 depends on a version of clojure.spec that uses
. See this commit for an example workaround.

When the

flag is set, some
AOT compilation issues will be deferred as runtime exceptions. You can try specifying this flag if
compilation fails. To avoid unexpected errors at runtime, don't use this flag for "production" builds.


to use HTTP libraries. HTTPS is available as of 1.0.0-RC7 (e.g.
) but requires additional configuration.


:jvm-opts ["-Dclojure.compiler.direct-linking=true"]
might allow for better compile-time optimizations.

This plugin doesn't shutdown GraalVM

build servers after builds, so that subsequent builds are slightly faster. You can set
:opts ["--no-server"]
to not spawn a build server at all, or use GraalVM's
command directly to manage build server(s).


GraalVM Native Image AOT Compilation

Native Clojure with GraalVM

Instant Netty Startup using GraalVM (and source)


You'll need Leiningen and GraalVM installed to build and test the plugin.

Issues and PRs are welcome!


Copyright © 2019 Taylor Wood.

Distributed under the MIT License.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.