Clj-pack – Package Clojure apps into self-contained binaries without GraalVM
1–7 of 7 posts
Re: Clj-pack – Package Clojure apps into self-contained binaries without GraalVM
#2The usual answer is GraalVM native-image, but in practice it means dealing with reflection configs, library incompatibilities, long build times, and a complex toolchain. For many projects it's more friction than it's worth.
clj-pack takes a different approach: it bundles a minimal JVM runtime (via jlink) with your uberjar into a single executable. The result is a binary that runs anywhere with zero external dependencies and full JVM compatibility — no reflection configs, no unsupported libraries, your app runs exactly as it does in development.
clj-pack build --input ./my-project --output ./dist/my-app ./dist/my-app # no Java needed How it works:
Detects your build system (deps.edn or project.clj)
Compiles the uberjar
Downloads a JDK from Adoptium (cached locally)
Uses jdeps + jlink to create a minimal runtime (~30-50 MB)
Packs everything into a single binary
The binary extracts on first run (cached by content hash), subsequent runs are instant.
Trade-off is honest: binaries are slightly larger than GraalVM output (~30-50 MB vs ~20-40 MB), and first execution has extraction overhead. But you get full compatibility and a simple build process in return.
Written in Rust, supports Linux and macOS (x64/aarch64).
Feedback and contributions welcome
Re: Clj-pack – Package Clojure apps into self-contained binaries without GraalVM
#3Re: Clj-pack – Package Clojure apps into self-contained binaries without GraalVM
#4I built a tool to solve a problem I kept hitting: deploying Clojure apps without requiring Java on the target machine.23:20:00 [3/101] The usual answer is GraalVM native-image, but in practice it means dealing with reflection configs, library incompatibilities, long build times, and a complex toolchain. For many projects it's more friction than it's worth. clj-pack takes a different approach: it bundles a minimal JVM…
Do you have the ability to crosscompile to other architectures/OS?
Do you have the ability to generate a plain executable? the jlink/jpackage route ends up generating an "installers" for each system, which i find hard/annoying to test and people are reluctant to install a program you send them
In the past ive ended up distributing an uberjar bc i didnt have the setup to test all the resulting bundles (esp MacOS which requires buying a separate machine). I also found JavaFX to be a bit inconsistent.. though its been a few years and maybe the situation has improved
Re: Clj-pack – Package Clojure apps into self-contained binaries without GraalVM
#5I built a tool to solve a problem I kept hitting: deploying Clojure apps without requiring Java on the target machine.23:20:00 [3/101] The usual answer is GraalVM native-image, but in practice it means dealing with reflection configs, library incompatibilities, long build times, and a complex toolchain. For many projects it's more friction than it's worth. clj-pack takes a different approach: it bundles a minimal JVM…
whats the advantage over just manually making a uberjar and using jlink/jpackage? Do you have the ability to crosscompile to other architectures/OS? Do you have the ability to generate a plain executable? the jlink/jpackage route ends up generating an "installers" for each system, which i find hard/annoying to test and people are reluctant to install a program you send them In the past ive ended up distributing an ub…
The main pain point jbundle solves is that jpackage generates installers (.deb, .rpm, .dmg, .msi), not plain executables. jbundle produces a single self-contained binary — just a shell stub concatenated with a compressed payload. You chmod +x it, distribute it, and the user runs ./app. No installation step, no system-level changes.
It also automates the full pipeline (detect build system → build uberjar → download JDK → jdeps → jlink → pack) so you don't need a JDK installed on the build machine — it fetches the exact version from Adoptium. Plus it includes startup optimizations like AppCDS (auto-created on first run, JDK 19+), CRaC checkpoints, and profile-tuned JVM flags for CLI vs server workloads.
Cross-compilation:
Yes — jbundle build --target linux-x64 (or linux-aarch64, macos-x64, macos-aarch64). Since the JAR is platform-independent, it just downloads the appropriate JDK runtime for the target OS/arch from Adoptium and bundles it. You can build a Linux binary from macOS and vice-versa.
Plain executable (not an installer):
That's exactly what jbundle produces. The output is a single file you can scp to a server or hand to someone. On first run it extracts the runtime and jar to ~/.jbundle/cache/ (keyed by content hash), so subsequent runs are instant. No .deb, no .dmg, no "install this first" — just a binary.
For the macOS testing concern: since it's a CLI binary (not a .app bundle), it doesn't require signing/notarization to run. And with --target macos-aarch64 you can build it from a Linux CI without needing a Mac.
Re: Clj-pack – Package Clojure apps into self-contained binaries without GraalVM
#6Earlier quoted context omitted.
whats the advantage over just manually making a uberjar and using jlink/jpackage? Do you have the ability to crosscompile to other architectures/OS? Do you have the ability to generate a plain executable? the jlink/jpackage route ends up generating an "installers" for each system, which i find hard/annoying to test and people are reluctant to install a program you send them In the past ive ended up distributing an ub…
Advantage over manual uberjar + jlink/jpackage: The main pain point jbundle solves is that jpackage generates installers (.deb, .rpm, .dmg, .msi), not plain executables. jbundle produces a single self-contained binary — just a shell stub concatenated with a compressed payload. You chmod +x it, distribute it, and the user runs ./app. No installation step, no system-level changes. It also automates the full pipeline (d…
The windows .exe will need to be generated on a windows VM then?
I will try to play around with it soon. Thank you for this. Getting executable directly sounds very handy :)
Re: Clj-pack – Package Clojure apps into self-contained binaries without GraalVM
#7Earlier quoted context omitted.
Advantage over manual uberjar + jlink/jpackage: The main pain point jbundle solves is that jpackage generates installers (.deb, .rpm, .dmg, .msi), not plain executables. jbundle produces a single self-contained binary — just a shell stub concatenated with a compressed payload. You chmod +x it, distribute it, and the user runs ./app. No installation step, no system-level changes. It also automates the full pipeline (d…
Ohhh okay! that actually cleared up a lot. Thank you (and sorry for getting back to you so late)! The windows .exe will need to be generated on a windows VM then? I will try to play around with it soon. Thank you for this. Getting executable directly sounds very handy :)
So unfortunately it looks like I'm stuck using `jpackage`