Multiple Java versions on Windows: pin JAVA_HOME per service
Keep JDK 17 and 21 side by side, pin JAVA_HOME in each Tomcat/service wrapper, and stop relying on a machine-wide PATH switch.
- Build
- 26100.33650
- Edition
- Standard / Datacenter
- Architecture
- x64
- Support
- Documentation (E1)
- Evidence
- E1Official documentation
- Verified
- 2026-10-29
- Page type
- Method
The short answer
Section titled “The short answer”Do not switch Java globally with setx JAVA_HOME and hope every service picks it up. Pin the JDK per service:
C:\Java\jdk-17\C:\Java\jdk-21\C:\apps\tomcat-a\setenv.bat -> JAVA_HOME=C:\Java\jdk-17C:\apps\tomcat-b\setenv.bat -> JAVA_HOME=C:\Java\jdk-21PATH is for your interactive shell. A Windows service gets its environment when it starts; changing the machine PATH does not retarget a running service.
Install side by side
Section titled “Install side by side”- Download Temurin/Microsoft OpenJDK zips for each LTS.
- Extract to versioned directories, not
C:\Program Files\Java\latest. - Do not add either
binto the machine PATH unless you need a default CLI. - Verify each:
& C:\Java\jdk-17\bin\java.exe -version& C:\Java\jdk-21\bin\java.exe -versionKeep the full path in scripts; it is the only unambiguous selector.
Tomcat per instance
Section titled “Tomcat per instance”Tomcat reads JAVA_HOME (or JRE_HOME) at startup. In each instance:
REM C:\apps\tomcat-a\bin\setenv.batset "JAVA_HOME=C:\Java\jdk-17"set "CATALINA_BASE=C:\apps\tomcat-a"REM C:\apps\tomcat-b\bin\setenv.batset "JAVA_HOME=C:\Java\jdk-21"set "CATALINA_BASE=C:\apps\tomcat-b"setenv.bat is per instance and survives upgrades better than editing catalina.bat. Keep CATALINA_HOME as the shared Tomcat install if you run multiple instances from one binary; keep CATALINA_BASE per instance (config, logs, webapps).
Windows service
Section titled “Windows service”If Tomcat runs as a service, the service wrapper must capture the right environment. With the Tomcat service manager:
tomcat10w.exe //ES//TomcatASet the Java tab / JVM path to the versioned jre/jdk, or use a wrapper that reads setenv.bat. After changing, restart the service and verify the actual process:
Get-CimInstance Win32_Process -Filter "Name='java.exe'" | Select-Object ProcessId, CommandLineThe command line should show the expected JDK path.
Verify the app, not just java -version
Section titled “Verify the app, not just java -version”Invoke-WebRequest http://localhost:8080/ -UseBasicParsing | Select-Object StatusCodeGet-Content C:\apps\tomcat-a\logs\catalina.*.log -Tail 50A Tomcat started with the wrong JDK may still boot, then fail on a class file version, TLS provider or GC option. Check the log’s “Java Home”/JVM line.
Upgrade path
Section titled “Upgrade path”- Add the new JDK beside the old.
- Test one instance with
setenv.bat. - Run the app’s test suite and check TLS/keystore compatibility.
- Move instances one at a time.
- Remove the old JDK after rollback is no longer needed.
Lab boundary
Section titled “Lab boundary”E1. The lab did not install a JDK or Tomcat; the commands are the per-service pattern to validate on Server 2025.