When multiple Java versions are installed on the same system, only one can be the default. The wrong default breaks builds, crashes application servers, and produces confusing “unsupported class file version” errors. Ubuntu and Debian use the update-alternatives system to manage this, but there are actually four different ways to control which Java version runs, each useful in different scenarios.
This guide covers all four methods: the system-wide update-alternatives command (interactive and scripted), JAVA_HOME overrides for specific applications, direct path execution for one-off commands, and per-project configuration with build tools. Every example was tested on Ubuntu 24.04 with five OpenJDK versions installed simultaneously (8, 11, 17, 21, and 25).
Current as of March 2026. Verified on Ubuntu 24.04 LTS with OpenJDK 8 (1.8.0_482), 11 (11.0.30), 17 (17.0.18), 21 (21.0.10), and 25 (25.0.2)
Prerequisites
- Ubuntu 24.04 / 22.04 or Debian 13 / 12
- Two or more Java versions installed (see Install JDK 25 on Ubuntu or Install JDK 25 on Debian)
- Root or sudo access
Check Installed Java Versions
Before switching, see what’s registered with the alternatives system:
sudo update-alternatives --list java
With five JDKs installed, the output lists each one:
/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
/usr/lib/jvm/java-11-openjdk-amd64/bin/java
/usr/lib/jvm/java-17-openjdk-amd64/bin/java
/usr/lib/jvm/java-21-openjdk-amd64/bin/java
/usr/lib/jvm/java-25-openjdk-amd64/bin/java
Check the current default:
java -version
The output shows which version is currently active:
openjdk version "25.0.2" 2026-01-20
OpenJDK Runtime Environment (build 25.0.2+10-Ubuntu-124.04)
OpenJDK 64-Bit Server VM (build 25.0.2+10-Ubuntu-124.04, mixed mode, sharing)
Method 1: Interactive Menu (update-alternatives –config)
The interactive method presents a numbered menu and lets you pick:
sudo update-alternatives --config java
The menu shows each version with its priority number. Higher priority means that version wins in auto mode. Type the selection number and press Enter:
There are 5 choices for the alternative java (providing /usr/bin/java).
Selection Path Priority Status
------------------------------------------------------------
* 0 /usr/lib/jvm/java-25-openjdk-amd64/bin/java 2511 auto mode
1 /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java 1081 manual mode
2 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1111 manual mode
3 /usr/lib/jvm/java-17-openjdk-amd64/bin/java 1711 manual mode
4 /usr/lib/jvm/java-21-openjdk-amd64/bin/java 2111 manual mode
5 /usr/lib/jvm/java-25-openjdk-amd64/bin/java 2511 manual mode
Press to keep the current choice[*], or type selection number:
Selection 0 (auto mode) always uses the highest-priority version. When you pick a specific number, the system enters manual mode and stays on your choice until you explicitly change it, even if a higher-priority version is installed later.
Do the same for the compiler:
sudo update-alternatives --config javac
Always switch java and javac to the same version. A mismatch (compiling with JDK 21, running with JDK 17) causes bytecode compatibility errors.
Method 2: Non-Interactive Switch (update-alternatives –set)
For scripts, CI pipelines, and automation, use --set to switch without prompts:
Switch to Java 17:
sudo update-alternatives --set java /usr/lib/jvm/java-17-openjdk-amd64/bin/java
sudo update-alternatives --set javac /usr/lib/jvm/java-17-openjdk-amd64/bin/javac
Verify:
java -version
Confirms Java 17 is now the default:
openjdk version "17.0.18" 2026-01-20
Here are the paths for each version on Ubuntu/Debian:
| Version | java Path | javac Path | Priority |
|---|---|---|---|
| Java 8 | /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java | /usr/lib/jvm/java-8-openjdk-amd64/bin/javac | 1081 |
| Java 11 | /usr/lib/jvm/java-11-openjdk-amd64/bin/java | /usr/lib/jvm/java-11-openjdk-amd64/bin/javac | 1111 |
| Java 17 | /usr/lib/jvm/java-17-openjdk-amd64/bin/java | /usr/lib/jvm/java-17-openjdk-amd64/bin/javac | 1711 |
| Java 21 | /usr/lib/jvm/java-21-openjdk-amd64/bin/java | /usr/lib/jvm/java-21-openjdk-amd64/bin/javac | 2111 |
| Java 25 | /usr/lib/jvm/java-25-openjdk-amd64/bin/java | /usr/lib/jvm/java-25-openjdk-amd64/bin/javac | 2511 |
Notice that Java 8’s java binary lives under jre/bin/ while Java 11+ moved it to bin/ directly. The javac binary is always under bin/.
To return to auto mode (highest priority wins):
sudo update-alternatives --auto java
sudo update-alternatives --auto javac
Method 3: JAVA_HOME Override
Some applications (Tomcat, Maven, Gradle, Elasticsearch) read JAVA_HOME instead of using whatever /usr/bin/java points to. You can set this per-session, per-user, or system-wide without touching alternatives at all.
Per-Session (Current Terminal Only)
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
$JAVA_HOME/bin/java -version
Java 17 runs regardless of what /usr/bin/java points to:
openjdk version "17.0.18" 2026-01-20
This only affects the current shell. Close the terminal and it’s gone.
Per-User (Persistent)
Add to ~/.bashrc or ~/.profile:
echo 'export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> ~/.bashrc
source ~/.bashrc
This lets one user run Java 21 while the system default stays on Java 25.
System-Wide (All Users)
Create a profile script that dynamically resolves JAVA_HOME from the current alternatives default:
echo 'export JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))' | sudo tee /etc/profile.d/java.sh
source /etc/profile.d/java.sh
echo $JAVA_HOME
Resolves to the current default automatically:
/usr/lib/jvm/java-25-openjdk-amd64
The dynamic approach keeps JAVA_HOME in sync when you switch versions with update-alternatives. For systemd service files where you need a fixed path, set JAVA_HOME explicitly in the unit file:
Environment="JAVA_HOME=/usr/lib/jvm/java-25-openjdk-amd64"
Method 4: Direct Path Execution
Run a specific Java version without changing any defaults. Use the full path:
/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java -version
Runs Java 8 without affecting the system default:
openjdk version "1.8.0_482"
This is useful for testing a specific version or running a legacy application that needs an older JDK while keeping the system default on the latest LTS.
/usr/lib/jvm/java-11-openjdk-amd64/bin/java -jar legacy-app.jar
/usr/lib/jvm/java-25-openjdk-amd64/bin/java -jar modern-app.jar
How Auto Mode and Priorities Work
Each registered alternative has a priority number. In auto mode, the highest priority wins. Ubuntu assigns priorities based on the version number:
| Version | Priority | Wins in Auto Mode? |
|---|---|---|
| Java 8 | 1081 | No |
| Java 11 | 1111 | No |
| Java 17 | 1711 | No |
| Java 21 | 2111 | No |
| Java 25 | 2511 | Yes (highest) |
When you run --config and pick a specific version, the system enters manual mode. It stays on your choice even when you install a new JDK with a higher priority. To go back to auto mode:
sudo update-alternatives --auto java
Check current mode with:
sudo update-alternatives --display java | head -3
The first line tells you whether auto or manual mode is active:
java - auto mode
link best version is /usr/lib/jvm/java-25-openjdk-amd64/bin/java
link currently points to /usr/lib/jvm/java-25-openjdk-amd64/bin/java
Switch All JDK Tools at Once
Switching java and javac individually is fine for two tools, but a JDK registers many alternatives: jar, javadoc, jshell, jlink, keytool, and more. To switch all of them at once, use a loop:
TARGET=/usr/lib/jvm/java-21-openjdk-amd64
for tool in java javac jar javadoc javap jcmd jdb jdeps jfr jlink jmod jps jshell jstack jstat keytool; do
path="$TARGET/bin/$tool"
[ -f "$path" ] && sudo update-alternatives --set $tool "$path" 2>/dev/null
done
java -version
This ensures all tools point to the same JDK version, avoiding subtle issues where java runs version 25 but jar points to version 17.
Register a Manually Installed JDK
JDKs installed from .deb packages (OpenJDK, Temurin) register themselves automatically. If you installed Oracle JDK from an RPM, a tarball, or a manual download, register it manually:
sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-25.0.2-oracle-x64/bin/java 1
sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk-25.0.2-oracle-x64/bin/javac 1
The last number is the priority. Set it lower than the system packages (which use 1081+) if you want it as a secondary option, or higher if you want it as the default.
To remove a registered alternative:
sudo update-alternatives --remove java /usr/lib/jvm/jdk-25.0.2-oracle-x64/bin/java
Verify the Configuration
After switching, always verify both the runtime and compiler match:
java -version
javac -version
echo $JAVA_HOME
If java and javac show different versions, switch them both to the same JDK. If JAVA_HOME points to a different version than java -version, an application that reads JAVA_HOME will use a different JDK than one that calls /usr/bin/java directly.
Check where /usr/bin/java actually points through the symlink chain:
readlink -f $(which java)
This resolves all symlinks and shows the final binary path.
Which Method to Use
| Scenario | Method |
|---|---|
| Change the system default permanently | update-alternatives --set or --config |
| Run a specific app with a different JDK | Set JAVA_HOME in the app’s systemd unit or wrapper script |
| Quick test with an older version | Use the direct path: /usr/lib/jvm/java-11-openjdk-amd64/bin/java |
| CI/CD pipeline version pinning | update-alternatives --set in the pipeline script |
| One user needs a different version | Set JAVA_HOME in ~/.bashrc |
| Multiple apps, each needing different JDKs | Set JAVA_HOME per systemd unit file, leave system default on latest |
For installing Java on your system, see our guides for JDK 25 on Ubuntu, JDK 25 on Debian, or JDK 25 on Rocky Linux / AlmaLinux.
automatically set default version without manually entering the value