How‑to Guide
Common tasks and recipes with javm.
Switch versions
- Use a specific version or range:
javm use temurin@21javm use zulu@~1.8.144(equivalent to>=1.8.144 <1.9.0)
- Without arguments, javm reads
.java-versionin the current directory:javm use
- Check the current version:
javm current
- Deactivate the current shell session:
javm deactivate- This removes javm-managed entries from
PATHand restores the previousJAVA_HOMEwhen available. It does not remove a JDK.
Use javm uninstall to remove a managed JDK, or javm unlink to remove a link to an external JDK.
Example:
javm use 21
java --version
javm deactivate
Choose how to select a JDK
| I want to... | Use |
|---|---|
| Switch the JDK in the current session | javm use <selector> |
| Run one process with a selected JDK | javm exec [--jdk <selector>] <command> [args...] |
| Choose a JDK for a project | Add .java-version, then run javm use in that project directory |
| Set the JDK used in new shells | javm default <selector> |
| Create a short name for a selector | javm alias <name> <selector> |
An explicit selector passed to javm use takes precedence over .java-version. Without an argument, javm use
reads .java-version from the current directory; it does not automatically switch versions when you enter a directory
and it does not fall back to javm default. The default is applied by the shell integration when a new shell starts,
and an alias is resolved when its name is used as a selector.
Run tools without shell integration
Use javm exec when an IDE, CI job, launcher, or non-interactive script needs a predictable JDK but cannot or should not
modify the parent shell environment:
javm exec ./gradlew build
javm exec mvn test
javm exec --jdk temurin@21 ./gradlew build
javm exec --jdk 21 mvn test
The process receives the selected JDK as JAVA_HOME and with its bin directory first in PATH. The current shell is
unchanged, javm init is not required, and the command plus all following arguments are passed directly without a shell
parser for native executables. Use --jdk <selector> for an explicit JDK; without it, the selector comes from
.java-version in the current directory. On Windows, .cmd and .bat wrappers use COMSPEC because they are
command-interpreter scripts.
Set default version for new shells
- Set the default:
javm default temurin@21
- Effect:
- Creates
default-versionin javm config dir. - The shell integration applies the saved selector when a new shell starts.
- Creates
Discover local JDKs
- Refresh discovery cache:
javm discover refresh(forces refresh)
- List discovered JDKs:
javm ls
- Show detailed information (source, name, vendor, architecture, path):
javm ls --detailsorjavm ls -d
Managed vs linked JDKs
A managed JDK is downloaded by javm install and, without --output, stored in javm's managed JDK directory. It can
be removed with javm uninstall.
A linked/system JDK already exists outside javm. javm link system@21.0.1 /path/to/jdk checks the expected Java
executable and registers a reference to it; it does not copy or install the JDK. javm unlink system@21.0.1 removes
only that reference and leaves the external installation in place.
The link is registered in javm's local JDK set, so javm ls --details lists it and javm use or javm which can
resolve its system@... name. The JDK itself remains external to javm.
The link name uses an exact version, while later selectors can use ranges:
- Link an existing (unmanaged) JDK:
javm link system@21.0.1 /path/to/jdk- The name must begin with
system@and use an exact semantic version.
- Show a link target:
javm link system@21.0.1
- Remove the link:
javm unlink system@21.0.1
- Use the registered link name with
javm useorjavm which:javm use system@21javm which system@~21
The path passed to link must contain the platform's Java executable: <path>/bin/java on Linux,
<path>/Contents/Home/bin/java on macOS, and <path>/bin/java.exe on Windows. If link reports that Java was not
found, check the path and pass the JDK root; on macOS, pass the bundle root rather than its nested Contents/Home.
Aliases
- Create or update an alias:
javm alias project temurin@21
- Resolve an alias value:
javm alias project
- Delete an alias:
javm unalias project
- Notes:
- Aliases are stored as
*.aliasfiles in the javm config dir. - Alias updates automatically relink the
jdk\aliassymlink to the best match.
- Aliases are stored as
List versions (local vs remote)
- Local/discovered:
javm ls- Filter by range:
javm ls "~21"
- Show detailed information:
javm ls --details - Remote (available to install):
javm ls-remote(defaults:--oscurrent,--archcurrent, the effectivejava.default_distribution, initiallytemurin, and--latest=major)- All distributions:
javm ls-remote --distribution=all - Filter by range:
javm ls-remote zulu@">=1.8 <1.9" - Latest per major (default):
javm ls-remote --latest=major - Latest per minor:
javm ls-remote --latest=minor - Latest per patch:
javm ls-remote --latest=patch
Configure the default distribution
The public configuration key java.default_distribution controls the distribution used by remote operations when
no distribution is specified explicitly. Its initial value is temurin.
- Inspect the effective value:
javm config listjavm config get java.default_distribution
- Choose another distribution:
javm config set java.default_distribution zulu
- Restore the initial default:
javm config unset java.default_distribution
After setting zulu, for example, javm install 21 and javm ls-remote 21 use Zulu. An explicit selector such as
zulu@21, or an explicit --distribution value for ls-remote, overrides the setting. This does not change local
discovery or make javm use 21 prefer a particular distribution among discovered JDKs.
Which JDK path
- Resolve path to a discovered JDK matching a selector:
javm which temurin@21
- Use
.java-versionif no argument is provided:javm which
- macOS: use
--hometo append/Contents/Homeso the result is suitable forJAVA_HOME:javm which temurin@21 --home
Coding agents
javm provides 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 guides agents to use project selectors such as .java-version and prefer javm exec <command> [args...] for builds, tests and other non-interactive Java processes.
Troubleshooting
javm: command not found after Unix installation
Symptom: the installer finishes, but the current shell cannot run javm.
Cause and check: install.sh puts the executable in $HOME/.local/bin without editing profiles.
command -v javm
ls -l "$HOME/.local/bin/javm"
Fix: run export PATH="$HOME/.local/bin:$PATH" for the current shell, then add the directory to the appropriate
profile if it should persist. See Verify the executable.
javm use does not change the effective Java
Symptom: javm use <selector> succeeds, but java or JAVA_HOME still points to another JDK.
Cause and check: the shell integration was not loaded, the shell was not restarted after setup, or the selected JDK is not the one being resolved.
javm which <selector>
javm current
command -v java
In PowerShell, use Get-Command java instead of command -v java. Load the integration from
Shell setup, restart the shell, and run javm use again.
shell integration is not active
Symptom: use or deactivate reports that shell integration is not active.
Cause and check: javm was invoked as a normal child process instead of through the wrapper generated by
javm init <shell>.
javm init <shell>
Apply the generated integration using the instructions for your shell in Shell setup.
Invalid or unmatched .java-version
Symptom: javm use without an argument fails, or the project version cannot be selected.
Cause and check: .java-version contains an invalid selector, is empty, or names a version that is not discovered.
cat .java-version
javm ls --details
javm use
In PowerShell, use Get-Content .java-version. Replace the file contents with a valid selector, then install or link a
matching JDK. See Selectors and ranges.
Selector has no result
Symptom: javm reports that a selector is not installed.
Cause and check: no discovered JDK matches the requested version, distribution, or range.
javm ls --details
javm ls-remote "temurin@21"
Install a matching JDK, link an existing one, or make the selector more specific, for example
temurin@21 instead of 21.
An unexpected distribution is selected
Symptom: an unqualified command such as javm install 21 or javm ls-remote 21 uses a different distribution
than expected.
Cause and check: unqualified selectors use the effective java.default_distribution, which is temurin unless
changed.
javm config get java.default_distribution
javm ls-remote 21 --distribution all
Use an explicit selector such as temurin@21, or change the setting with javm config set java.default_distribution <distribution>. See Configuration.
A local JDK does not appear in javm ls
Symptom: a JDK installed outside javm is not listed or cannot be selected.
Cause and check: the installation is outside the configured discovery locations, or discovery has not been run again after the installation changed.
javm ls --details
javm discover refresh
For an external JDK at a known path, register it with javm link system@<exact-version> <path>. See
link for the expected platform layout.
javm link rejects the JDK directory
Symptom: link reports that the Java executable was not found or the path could not be inspected.
Cause and check: the argument is not the JDK root for the current operating system, or its expected Java executable is missing.
ls -l /path/to/jdk/bin/java
# macOS: ls -l /path/to/jdk/Contents/Home/bin/java
# PowerShell: Test-Path 'C:\path\to\jdk\bin\java.exe'
Pass the JDK root to javm link system@<exact-version> <path>. Check the Linux, macOS, or Windows layout in the
link reference; on macOS, pass the bundle root rather than Contents/Home.
uninstall versus unlink
Symptom: javm uninstall system@... is rejected, or a link is removed when you intended to remove a JDK.
Cause and check: uninstall removes a managed JDK; unlink removes only the reference to an external JDK.
javm ls --details
javm link system@21.0.1
Use javm uninstall <selector> for a managed JDK and javm unlink system@<version> for a linked JDK. See
Managed vs linked JDKs.
The default is not applied in a new shell
Symptom: javm default <selector> succeeds, but a new shell does not activate that JDK.
Cause and check: the shell profile is not loading javm init, a static integration was generated before the default
was configured, or the default selector has no matching JDK.
javm ls --details
javm init <shell>
Ensure the profile loads the integration, regenerate a static integration if you use one, and start a new shell. See Set default version for new shells and Shell setup.
See all commands with javm --help; use javm --debug for additional diagnostic logs.