Show HN: Jolta – Automatic per-project JDK switching (like Volta, for Java)

pdxdave1 pts0 comments

Jolta — the hands-off Java version manager

$ cd my-service<br>$ jolta pin 21 # writes .java-version<br>$ java -version # → OpenJDK 21, automatically

$ cd ../legacy-app # has .java-version with "8"<br>$ java -version # → 1.8 — no sdk use, no jenv shell, no export JAVA_HOME

Install

Prebuilt binaries for every platform — no Rust toolchain, no runtime, nothing to<br>compile.

macOS Apple Silicon & Intel

Homebrew:

brew install OneAppPlatform/tap/jolta<br>jolta setup

Or the one-line installer:

curl -fsSL https://raw.githubusercontent.com/OneAppPlatform/jolta/main/install.sh | sh

Linux x86_64 & arm64 · static musl, any distro

One-line installer:

curl -fsSL https://raw.githubusercontent.com/OneAppPlatform/jolta/main/install.sh | sh

Or Homebrew on Linux:

brew install OneAppPlatform/tap/jolta<br>jolta setup

Windows x86_64

Download jolta-x86_64-pc-windows-msvc.zip from the<br>releases page, unzip, then:

.\jolta.exe setup<br>Follow the two printed steps: add the shims directory to your user<br>PATH, and add the PowerShell hook line to your $PROFILE.

Verify with a new shell:

jolta doctor

How it works

Jolta puts lightweight shims on your PATH<br>(java, javac, jar, jshell, …) — each one a<br>link to the single jolta binary. Every invocation walks up from the current directory to<br>the nearest .java-version, resolves an installed JDK for it, sets<br>JAVA_HOME, and execs the real tool. Resolution takes ~2 ms, invisible<br>next to JVM startup.

If a project pins a version you don't have, the first invocation<br>downloads it on demand (Volta-style) from the distro's official endpoint —<br>never triggered by merely cd-ing around, safe under parallel builds, and<br>disabled with JOLTA_NO_AUTO_INSTALL=1. Restricted networks can set<br>JOLTA_DOWNLOAD_BASE to serve JDKs from an internal mirror<br>({base}/{vendor}/{version}/{os}-{arch}.{ext}) instead of the vendor<br>endpoints.

Jolta also finds the JDKs you already have: Homebrew,<br>/Library/Java/JavaVirtualMachines, SDKMAN, /usr/lib/jvm,<br>Windows Program Files, and JAVA_HOME__* environment<br>variables.

Pinning

The pin is a plain .java-version file (compatible with jenv and asdf) —<br>commit it to your repo:

jolta pin 21 # this project uses Java 21<br>jolta default 21 # global fallback when a project has no pin<br>Resolution order: JOLTA_JAVA_VERSION env var → nearest<br>.java-version walking up → jolta default → system default JDK.<br>On a machine with no JDK at all, the first java run installs the latest<br>LTS (Temurin) and sets it as your default.

A major pin (21) matches the highest installed build of that major.<br>An exact pin (21.0.2) means exact — never silently substituted, and<br>auto-installed precisely. .sdkmanrc files (java=21.0.2-tem)<br>are honored with vendor mapping when no .java-version claims the<br>directory. jolta catalog (alias search) shows what every distro<br>publishes — latest per distro, per-major comparisons, and @v prefix<br>filters like temurin@21.0 — with installed versions marked.

Distros

A spec can name a JDK distribution as distro@version or<br>distro-version:

jolta pin corretto@21 # this project wants Amazon Corretto 21<br>jolta install graalvm@25 # explicitly fetch a GraalVM JDK

Downloadable: temurin (default), corretto,<br>graalvm, oracle, zulu.

Recognized when matching: those five plus<br>openjdk (Homebrew builds).

A distro-qualified pin is strict — corretto-21 picks an installed<br>Corretto 21 over a Homebrew OpenJDK 21, and auto-installs Corretto if missing.<br>A bare 21 matches any distro.

Point releases: jolta update checks jolta-managed JDKs against the<br>vendors (via redirect filenames — no downloads) and jolta upgrade<br>fetches newer builds and prunes superseded ones. Homebrew/system JDKs stay with<br>their own package managers; resolution picks the highest build of a major, so<br>their upgrades apply automatically.

JAVA_HOME, Maven & Gradle

Two mechanisms keep JAVA_HOME correct:

Shims export it for everything they exec.

The shell hook (installed by setup; zsh, bash, and PowerShell)<br>re-resolves it every time you change directory — covering tools that read<br>JAVA_HOME directly, like Maven, Gradle, and IDEs launched from a<br>terminal. If a pin can't be satisfied the hook unsets<br>JAVA_HOME, so builds fail loudly rather than silently using the wrong<br>JDK.

Don't export JAVA_HOME manually in your profile — the hook owns it. In<br>scripts and CI, use jolta exec mvn ... or<br>eval "$(jolta env)". jolta doctor flags stale values.

Commands

CommandWhat it does<br>jolta setupInstall shims + shell profile setup<br>jolta pin Write .java-version in the current directory<br>jolta default Set the global fallback version<br>jolta install Download a JDK (21, corretto@21, graalvm@25)<br>jolta catalog [x]The JDK catalog: latest per distro/major, @v prefix filters (alias search)<br>jolta updateCheck jolta-managed JDKs for newer point releases<br>jolta upgrade [spec]Upgrade jolta-managed JDKs, pruning old builds<br>jolta uninstall Remove a jolta-managed JDK<br>jolta listList visible JDKs with distro, star the active one<br>jolta jdksMachine-readable list: major / version / distro /...

jolta java version distro java_home install

Related Articles