How to Install Java (OpenJDK) on Windows Server 2025
Java remains one of the most widely deployed programming runtimes in enterprise environments, powering everything from web applications and microservices to big data platforms and enterprise middleware. Windows Server 2025 has no built-in Java runtime, so administrators must choose an OpenJDK distribution and install it manually. The good news is that the modern OpenJDK ecosystem has matured significantly: multiple vendors — including Microsoft, Eclipse Adoptium, and Azul Systems — provide fully tested, production-grade builds of OpenJDK for Windows as MSI installers that integrate cleanly with Windows services and Group Policy. This tutorial covers selecting a distribution, performing both interactive and silent installations, configuring JAVA_HOME and the system PATH, managing multiple JDK versions, verifying the installation, using keytool for certificate management, running JVM diagnostics with jps and jstack, and an introduction to GraalVM as a high-performance alternative.
Prerequisites
- Windows Server 2025 with administrative privileges
- PowerShell 5.1 or PowerShell 7.x
- Internet access for downloading JDK packages, or an internal file share with pre-downloaded MSIs
- At least 500 MB of free disk space per JDK version
Step 1: Choose an OpenJDK Distribution
All major OpenJDK distributions are compatible with Windows Server 2025 and pass the OpenJDK Technology Compatibility Kit (TCK). Your choice comes down to support terms, patch cadence, and licensing preferences.
- Microsoft Build of OpenJDK — Free, Microsoft-supported builds for Java 11 and Java 21 LTS. Available via
wingetand as MSI. Best choice for Azure-integrated environments. - Eclipse Temurin (Adoptium) — Community-driven, TCK-verified builds for all active LTS releases. Available from
adoptium.netas MSI. No-cost with community support. - Azul Zulu — Commercial and community builds from Azul Systems. Includes support for Java SE 8, 11, 17, 21, and 23. Available as MSI with optional extended support contracts.
- Oracle OpenJDK — Reference builds from Oracle. Free under the GPL+Classpath Exception but no long-term commercial support without an Oracle Java SE subscription.
Step 2: Install the Microsoft Build of OpenJDK via winget
The fastest installation path on Windows Server 2025 is through winget, which downloads and silently installs the chosen JDK with a single command.
# Search for available Microsoft OpenJDK packages
winget search Microsoft.OpenJDK
# Install Microsoft OpenJDK 21 (LTS)
winget install Microsoft.OpenJDK.21 --silent --accept-package-agreements --accept-source-agreements
# Install Eclipse Temurin JDK 21
winget install EclipseAdoptium.Temurin.21.JDK --silent --accept-package-agreements
# Install Azul Zulu JDK 21
winget install Azul.Zulu.21.JDK --silent --accept-package-agreements
# Verify installation path
Get-Command java | Select-Object -ExpandProperty Source
java -version
Step 3: Silent MSI Installation for Automated Deployment
For automated deployments using configuration management tools (Ansible, Chef, DSC, or custom scripts), use the MSI installer in silent mode. This approach works without winget and is suitable for airgapped servers.
# Download Eclipse Temurin JDK 21 MSI (example filename)
$msiUrl = "https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.5%2B11/OpenJDK21U-jdk_x64_windows_hotspot_21.0.5_11.msi"
$msiPath = "C:TempOpenJDK21.msi"
Invoke-WebRequest -Uri $msiUrl -OutFile $msiPath -UseBasicParsing
# Silent install — sets JAVA_HOME and updates PATH automatically
Start-Process msiexec.exe -ArgumentList `
"/i `"$msiPath`" /quiet /norestart ADDLOCAL=FeatureMain,FeatureEnvironment,FeatureJarFileRunWith,FeatureJavaHome" `
-Wait -PassThru
# Verify
$env:JAVA_HOME = [Environment]::GetEnvironmentVariable("JAVA_HOME", "Machine")
Write-Host "JAVA_HOME set to: $env:JAVA_HOME"
java -version
Step 4: Manually Configure JAVA_HOME and PATH
Some distributions or ZIP-based installations do not set environment variables automatically. Set them manually to ensure Java tools are accessible from all shells and services.
# Determine where the JDK was installed
$jdkPath = "C:Program FilesEclipse Adoptiumjdk-21.0.5.11-hotspot"
# Set JAVA_HOME system-wide
[Environment]::SetEnvironmentVariable("JAVA_HOME", $jdkPath, "Machine")
# Add JAVA_HOMEbin to the system PATH
$currentPath = [Environment]::GetEnvironmentVariable("PATH", "Machine")
if ($currentPath -notlike "*$jdkPathbin*") {
[Environment]::SetEnvironmentVariable(
"PATH",
"$jdkPathbin;$currentPath",
"Machine"
)
Write-Host "PATH updated."
}
# Using setx for compatibility (also sets user-level variables)
setx JAVA_HOME "$jdkPath" /M
setx PATH "$jdkPathbin;%PATH%" /M
# Reload environment in the current PowerShell session
$env:JAVA_HOME = [Environment]::GetEnvironmentVariable("JAVA_HOME", "Machine")
$env:PATH = [Environment]::GetEnvironmentVariable("PATH", "Machine")
# Confirm
Write-Host "JAVA_HOME: $env:JAVA_HOME"
java -version
javac -version
Step 5: Managing Multiple JDK Versions
Enterprise environments often require multiple Java versions simultaneously — for example, Java 11 for legacy Spring Boot applications and Java 21 for newer microservices. Use a simple JAVA_HOME switching script to toggle between them.
# Install both JDK 11 and JDK 21 to named directories
$jdk11 = "C:Javajdk-11.0.24"
$jdk21 = "C:Javajdk-21.0.5"
# Function to switch the active JDK
function Switch-JavaVersion {
param([ValidateSet("11","21")][string]$Version)
$target = switch ($Version) {
"11" { $jdk11 }
"21" { $jdk21 }
}
[Environment]::SetEnvironmentVariable("JAVA_HOME", $target, "Machine")
$path = [Environment]::GetEnvironmentVariable("PATH", "Machine")
# Remove any existing javabin entries and prepend the new one
$cleanPath = ($path -split ";" | Where-Object { $_ -notmatch "Java\jdk" }) -join ";"
[Environment]::SetEnvironmentVariable("PATH", "$targetbin;$cleanPath", "Machine")
$env:JAVA_HOME = $target
$env:PATH = "$targetbin;$cleanPath"
Write-Host "Switched to Java $Version"
java -version
}
# Switch to Java 21
Switch-JavaVersion -Version "21"
# Switch to Java 11
Switch-JavaVersion -Version "11"
Step 6: Java Keytool for Certificate Management
Java applications use the JDK’s keytool utility to manage a Java KeyStore (JKS) containing trusted certificates and private key pairs. This is commonly needed when connecting to HTTPS services, configuring TLS for Tomcat, or trusting internal certificate authorities.
# List certificates in the default JDK cacerts truststore
keytool -list -cacerts -storepass changeit
# Import a corporate CA certificate into the JDK truststore
keytool -import -trustcacerts `
-alias "CorpRootCA" `
-file "C:Certscorp-root-ca.crt" `
-keystore "$env:JAVA_HOMElibsecuritycacerts" `
-storepass changeit `
-noprompt
# Generate a new self-signed certificate for testing
keytool -genkeypair -alias myserver `
-keyalg RSA -keysize 2048 `
-validity 365 `
-keystore "C:Javamyserver.jks" `
-storepass changeit `
-dname "CN=myserver.example.com, OU=IT, O=MyOrg, L=Seattle, S=WA, C=US"
# Export the certificate to a .cer file
keytool -exportcert -alias myserver `
-keystore "C:Javamyserver.jks" `
-storepass changeit `
-file "C:Javamyserver.cer" -rfc
Step 7: JVM Diagnostics with jps and jstack
The JDK includes several diagnostic tools that are invaluable for troubleshooting running Java applications. jps lists running JVM processes, and jstack captures thread dumps for deadlock analysis.
# List all running Java processes with their process IDs and main class names
jps -lv
# Capture a thread dump from a specific JVM process (replace 1234 with actual PID)
jstack 1234 > "C:Logsthreaddump-$(Get-Date -Format 'yyyyMMdd-HHmmss').txt"
# Collect heap information
jmap -heap 1234
# Monitor JVM garbage collection in real-time
jstat -gcutil 1234 1000 10
# Use jcmd for a unified diagnostic interface
jcmd 1234 VM.flags
jcmd 1234 Thread.print
jcmd 1234 GC.heap_info
Step 8: GraalVM as a High-Performance Alternative
GraalVM from Oracle is an alternative JDK that includes a polyglot runtime and the Native Image compiler, which compiles Java applications ahead-of-time into standalone executables with faster startup and lower memory footprint — particularly attractive for microservices and serverless workloads.
# Install GraalVM for JDK 21 via winget
winget install Oracle.GraalVM.21 --silent --accept-package-agreements
# Set JAVA_HOME to GraalVM
$graalHome = "C:Program FilesGraalVMgraalvm-jdk-21"
[Environment]::SetEnvironmentVariable("JAVA_HOME", $graalHome, "Machine")
$env:JAVA_HOME = $graalHome
$env:PATH = "$graalHomebin;$env:PATH"
# Verify GraalVM version
java -version
# Output: java version "21.0.x" ... GraalVM CE ...
# Compile a Java application to a native executable
# Requires Visual Studio Build Tools with C++ workload on Windows
native-image -jar MyApp.jar -o MyApp-native
# Run the native image (no JVM required)
.MyApp-native.exe
Conclusion
Installing Java on Windows Server 2025 is a straightforward process made even easier by winget and vendor-provided MSI packages that handle JAVA_HOME and PATH configuration automatically. For enterprise deployments, silent MSI installations integrate cleanly with automated provisioning workflows. Managing multiple JDK versions through environment variable switching is a practical approach that accommodates legacy and modern Java applications on the same server. The JDK’s built-in diagnostic tools — jps, jstack, jstat, and jcmd — provide deep visibility into running JVM processes without requiring any additional software. For teams requiring maximum startup performance and memory efficiency, GraalVM’s Native Image compilation offers a compelling alternative to the standard JVM runtime. With these techniques in place, your Windows Server 2025 environment is well-equipped to run any Java-based workload reliably in production.