If you work on more than one Java codebase, you've probably lived this: one service targets JDK 17, another needs 21, and there's a legacy app that will only ever build on 11. Every context switch means resetting JAVA_HOME, or remembering to run a version-switch command, or discovering ten minutes into a weird compiler error that you're on the wrong JDK.
There are good tools for this already — SDKMAN!, jenv, asdf, mise. I've used most of them. I built Jolta because I wanted something that leaned all the way into automatic, per-project switching with as close to zero ceremony as possible, and that treated Windows as a real target. This post is the honest tour, including where the existing tools might still suit you better.
Full disclosure: Jolta is my project. MIT-licensed, on GitHub at OneAppPlatform/jolta.
The idea, borrowed from Node
Volta solved this nicely in the Node world: you pin a tool version per project, and the right one is used automatically because your node/npm are actually lightweight shims that resolve the correct version at call time. No cd hook, no "did I remember to switch" ritual.
Jolta applies that model to the JDK.
bash
cd my-service
jolta pin 21
java -version # → OpenJDK 21, automatically
jolta pin 21 writes a .java-version file to the project. From then on, when you invoke java, javac, jar, jshell, and friends, Jolta's shims walk up the directory tree, find the nearest .java-version, and dispatch to the right JDK. Overhead is sub-5ms per call. If the pinned JDK isn't installed yet, Jolta downloads it on first use.
How resolution works
Jolta resolves the version to use in this order:
An explicit environment variable, if set
The project's .java-version file (found by walking up the tree)
Your global default (jolta default 21)
The system JDK
You can also pin a specific distribution when you care:
bash
jolta pin corretto@21
jolta default 21
Why you might pick it (and why you might not)
Where Jolta tries to be different:
Automatic, not manual. The whole point is that you don't run a switch command per directory — the shims handle it.
Compatible on day one. It reads the same .java-version files as jenv and asdf, and recognizes SDKMAN!'s .sdkmanrc. Migration is mostly "install Jolta and keep your existing files."
Single dependency-free Rust binary. One static binary is both the CLI and the shims. Nothing to install into your shell beyond running jolta setup.
Windows is first-class, alongside macOS (Apple Silicon + Intel) and Linux (x86_64 + arm64).
Multiple distributions: Temurin, Corretto, GraalVM, Oracle, Zulu.
Where the incumbents may still win for you:
SDKMAN! manages far more than JDKs (Gradle, Maven, Kotlin, Scala, …). If you want one tool for your whole JVM toolchain, that breadth is real.
mise / asdf are polyglot across many languages, so if you also manage Node/Python/Ruby versions you may prefer one manager for everything.
Those projects are mature and battle-tested; Jolta is young.
Installing
macOS (Homebrew):
bash
brew install OneAppPlatform/tap/jolta && jolta setup
Linux/macOS (one-liner):
bash
curl -fsSL https://raw.githubusercontent.com/OneAppPlatform/jolta/main/install.sh | sh
Windows: download the executable from the releases page and run jolta setup.
Then:
bash
jolta setup # install shims + shell config
jolta catalog # browse available versions
jolta pin 21 # pin this project
jolta doctor # diagnose config issues
Feedback wanted
It's early, and the most useful thing right now is critical feedback from people with real multi-JDK setups. If you try it, I'd love to hear where the resolution logic surprised you, which vendors/distributions you need, and what would have to be true for you to switch from your current setup. Issues and comments both welcome: github.com/OneAppPlatform/jolta.
Top comments (0)