javm is a fast, cross‑platform Java version manager (fork of jabba) focused on a frictionless install
and switch workflow for JDKs on Linux, macOS and Windows.
Features
- Install & switch between multiple JDK distributions (Zulu, OpenJDK, GraalVM, Temurin, etc.) using semver ranges
- Per‑project JDK via
.java-version
- Remote discovery through the Foojay DiscoAPI
- Supports semantic version ranges (
1.8.x, ~17.0.2, >=21 <22)
- Clean removal (
uninstall, deactivate) without touching system JDK
- Static Go binary: fast cold start and no additional dependency
Installation
You can install javm using the provided install scripts. They automatically detect your platform and architecture, and support different release channels:
latest: install from the latest GitHub Release (default)
nightly: install the most recent development build
- Specific version: e.g.
v0.1.0
Linux and macOS
By default, installs the latest release to $HOME/.local/bin/javm.
To install the latest release:
curl -fsSL https://javm.dev/install.sh | bash
To install a specific version or channel:
curl -fsSL https://javm.dev/install.sh | bash -s nightly
curl -fsSL https://javm.dev/install.sh | bash -s v0.1.0
To also verify build provenance with an authenticated GitHub CLI:
curl -fsSL https://javm.dev/install.sh | JAVM_VERIFY_ATTESTATION=1 bash
Windows
By default, installs the latest release to %LOCALAPPDATA%\Programs\javm.
To install the latest release:
irm https://javm.dev/install.ps1 | iex
To install a specific version or channel:
iex "& { $(irm https://javm.dev/install.ps1) } nightly"
iex "& { $(irm https://javm.dev/install.ps1) } v0.1.0"
To also verify build provenance with an authenticated GitHub CLI:
$env:JAVM_VERIFY_ATTESTATION = "1"
irm https://javm.dev/install.ps1 | iex
Installation verification
The installers always verify the SHA-256 checksum before extracting a release or nightly archive. Nightly installation
requires an authenticated GitHub CLI because GitHub Actions artifacts are downloaded with gh run download.
Provenance verification is opt-in because gh attestation verify uses the GitHub CLI authentication and network
configuration. Merely having gh installed never causes the stable installer to execute it. Set
JAVM_VERIFY_ATTESTATION=1 to request verification; in that mode, a missing or unauthenticated gh, an unexpected
repository identity, or an invalid attestation aborts installation. See ATTESTATION.md for the trust
model and manual verification instructions.
Shell Setup
To enable javm in your shell, initialize it with javm init <shell> as shown
for your shell below.
Bash
Add this to your ~/.bashrc or ~/.zshrc:
eval "$(javm init bash)"
Zsh
Add this to your ~/.zshrc:
eval "$(javm init zsh)"
Fish
Add this to your ~/.config/fish/config.fish:
javm init fish | source
PowerShell
Add this to your profile file:
iex "$(javm init pwsh)"
[!TIP]
To check the path to your PowerShell profile file, run:
echo $PROFILE
Command Prompt (CMD)
Generate the CMD wrapper in a directory separate from javm.exe:
mkdir "%LOCALAPPDATA%\javm\cmd" 2>NUL
javm init cmd > "%LOCALAPPDATA%\javm\cmd\javm.cmd"
set "PATH=%LOCALAPPDATA%\javm\cmd;%PATH%"
Keep that wrapper directory before the directory containing javm.exe in your
user PATH, then open a new Command Prompt. CMD checks .exe before .cmd
inside the same directory, so placing both files together bypasses the wrapper
and prevents use and deactivate from changing the current session.
When invoking javm from another batch file, use call javm ... so control
returns to the calling script:
call javm use 21
call javm deactivate
Nushell
Generate the script file using javm init nu:
mkdir ~/.local/share/javm
javm init nu | save -f ~/.local/share/javm/javm.nu
Add this to your config.nu (you can find it by running $nu.config-path):
source ~/.local/share/javm/javm.nu
Post Setup
After editing your profile or config, restart your shell or reload your profile to apply the changes.
Usage
Listing / Searching
javm ls-remote # list all available JDKs
javm ls-remote zulu@~1.8.60 # narrow by distribution & semver range
javm ls-remote "*@>=1.6.45 <1.9" --latest=minor # show only latest minors
Installing
javm install zulu@1.8 # Zulu OpenJDK 8 (latest matching)
javm install zulu@~1.8.144 # same as zulu@>=1.8.144 <1.9.0
javm install temurin@17 # Temurin 17 LTS
javm install graalvm@21 # GraalVM
javm install openjdk@21 # Upstream OpenJDK
To install a JDK in a new directory outside the managed JAVM_HOME, use
--output. The destination must not exist, and the resulting JDK is unmanaged
until it is linked explicitly:
javm install temurin@21 --output /opt/jdks/temurin-21
Using / Switching
javm ls # list installed
javm ls --details # include vendor, architecture and path
javm use zulu@1.8
javm use temurin@17
javm deactivate # restore previous JAVA_HOME / PATH
Per‑Project Version (.java-version)
Create a .java-version in your project root:
17
Then:
javm use # picks version from .java-version
Run a command with a JDK
Run a child process with JAVA_HOME and PATH configured for a selected JDK;
the current shell is unchanged and shell integration is not required:
javm exec java --version
javm exec ./gradlew test
javm exec mvn test
javm exec --jdk 21 java --version
javm exec --jdk temurin@21 ./gradlew build
Without --jdk, javm exec reads the selector from .java-version in the
current directory. The child command and all of its arguments are passed
through unchanged.
Aliases
javm alias default 17 # sets default JDK for new shells
javm alias default # shows the alias value
javm unalias default
Inspecting and configuring
javm current # show the JDK selected in the current shell
javm which temurin@21 # show the selected JDK path
javm which --home temurin@21 # use the platform-specific JAVA_HOME path
javm config list # list effective configuration
javm config get java.default_distribution
javm config set java.default_distribution temurin
javm config unset java.default_distribution
javm discover refresh # refresh the discovery cache
javm default temurin@21 # set the default for newly initialized shells
Linking Existing System JDK
javm link system@1.8.72 /Library/Java/JavaVirtualMachines/jdk1.8.0_72.jdk
Uninstall
javm uninstall zulu@1.8
Exit codes
javm uses stable exit codes so scripts can distinguish common failure
classes:
| Code |
Meaning |
| 0 |
Success, including --help |
| 1 |
Unexpected or local operation failure |
| 2 |
Invalid command usage or arguments |
| 3 |
Requested JDK or other resource was not found |
| 4 |
Remote service or download failure |
| 124 |
Operation timed out |
| 130 |
Interrupted with Ctrl+C |
Coding agents
javm includes an agent skill for coding agents that need to run Java tools with a predictable JDK.
Install it with:
npx skills add felipebz/javm --skill javm -g
The skill teaches agents to prefer javm exec for builds, tests and other non-interactive Java commands.
Development
Prerequisite: Go 1.27
git clone https://github.com/felipebz/javm
cd javm
go test ./...
go build -o javm .
./javm --help
FAQ
Q: Does it override my system Java?
No. javm use adjusts your shell PATH and JAVA_HOME in that session.
Q: How do I revert to the system JDK?
Run javm deactivate or open a new shell (if no default alias points elsewhere).
Q: Can I pin a project JDK?
Yes: Create a .java-version with the JDK version then javm use inside that directory.
Q: Why not install “globally”?
Global changes (e.g. system alternatives, registry edits) vary by OS and often require sudo/admin; javm keeps things user‑scoped and portable.
License
Apache-2.0. See LICENSE.