Which Java version do I need? An overview for servers
Which Java version your application really needs, which one your distribution actually offers, and how to run several versions in parallel and switch between them cleanly.
Java is rarely an end in itself on a server. It is there because a Minecraft server, a Tomcat, a Jenkins or a search index demands it. That is exactly why the question is never "which Java is the best one", but "which Java does this particular application expect, and can I even get it on this particular operating system". This article answers both: with support timelines, with an availability table that we measured in real containers, and with the practical side of running several versions in parallel.
The short version: which release for which purpose
If you are short on time, this is the decision:
- New project, free choice: Java 21. It is currently the most broadly supported LTS line, packaged and ready to use in Debian 13 and in every current Ubuntu release, and practically every current piece of server software runs on it.
- Minecraft 1.20.5 to 1.21.11: Java 21. Mandatory, no leeway.
- Minecraft 26.1 and newer: Java 25.
- Older application whose documentation says "Java 17": take Java 17, not "17 or newer". With modloaders and plugin systems, "newer" is often not true.
- Java 8 or 11: only when a legacy application forces it. Both belong on your list of things that have to be replaced.
- Java 25: the newest LTS line, a sensible choice for new deployments, but check first whether your framework is already officially cleared for it.
For the installation itself we have separate guides: Installing Java 17 on Debian and Installing Java 21 on Debian. This article is the map above them.
What LTS means and how long the versions are maintained
A new Java major release appears every six months. The vast majority of them are dead after exactly six months: Java 22, 23, 24 and 26 stop getting security updates the moment the next release arrives. You do not want those on a server.
Only the LTS versions (Long Term Support) are interesting. They have appeared every two years since 2021: 8, 11, 17, 21, 25, and Java 29 is planned next for September 2027. Only these lines receive quarterly security updates over a span of years.
The distinction between Oracle and the free builds matters. For server use you will normally reach for OpenJDK from the distribution package or for Eclipse Temurin. Both are free to use, and in some cases maintenance there runs considerably longer than under Oracle's free license.
| Version | Released | Status | Temurin builds at least until |
|---|---|---|---|
| Java 8 | 2014 | LTS, legacy | December 2030 |
| Java 11 | 2018 | LTS, winding down | October 2027 |
| Java 17 | 2021 | LTS, widely used | October 2027 |
| Java 21 | 2023 | LTS, default choice | December 2029 |
| Java 25 | 2025 | LTS, current | September 2031 |
The row for Java 17 surprises a lot of people: only until October 2027. Java 17 feels new, but it is already the second to last LTS generation. If you are setting up a system today that is meant to run for three years, plan with 21 or 25 from the start.
Which Java version your distribution has in its own repository
This is where most guides on the internet fall apart: they write "apt install openjdk-17-jre-headless" and assume that this works everywhere. It does not. We measured the package situation on 27 July 2026 in fresh containers.
| Package | Debian 13 | Debian 12 | Ubuntu 24.04 | Ubuntu 22.04 |
|---|---|---|---|---|
| openjdk-8-jre-headless | missing | missing | 8u492 | 8u492 |
| openjdk-11-jre-headless | missing | missing | 11.0.31 | 11.0.31 |
| openjdk-17-jre-headless | missing | 17.0.19 | 17.0.19 | 17.0.19 |
| openjdk-21-jre-headless | 21.0.11 | missing | 21.0.11 | 21.0.11 |
| openjdk-25-jre-headless | 25.0.3 | missing | 25.0.3 | 25.0.3 |
Read the Debian columns twice. Debian 12 knows Java 17 and nothing else. Debian 13 no longer knows Java 17, but does have 21 and 25. So there is not a single version in the official Debian sources that exists on both releases. Anyone writing a deployment script that has to run on bookworm and trixie cannot rely on one package name.
Ubuntu is more pleasant in this respect: all five LTS lines sit next to each other in the repository, on 22.04 just as on 24.04. If you need a system where a legacy application on Java 8 and a modern service on Java 21 run at the same time, Ubuntu is the shortcut.
When in doubt, check for yourself instead of guessing:
apt update
apt-cache policy openjdk-21-jre-headless
apt-cache search openjdk-
If "Candidate" shows (none), the package does not exist on this distribution. On the Red Hat family (AlmaLinux, Rocky, RHEL, Oracle Linux) the packages carry different names, and you list them like this:
dnf list java-\*-openjdk-headless
dnf install -y java-21-openjdk-headless
For the overview use dnf list and not dnf list available. The latter hides packages that are already installed, so after installing Java 21 it no longer shows up in the list at all and you end up looking in the wrong place. The situation in the Red Hat family, likewise measured in fresh containers:
| Package | AlmaLinux 10 | AlmaLinux 9, Rocky 9, Oracle 9 |
|---|---|---|
| java-1.8.0-openjdk-headless | missing | available |
| java-11-openjdk-headless | missing | available |
| java-17-openjdk-headless | missing | available |
| java-21-openjdk-headless | 21.0.12 | 21.0.11 to 21.0.12 |
| java-25-openjdk-headless | available | available |
AlmaLinux 10 has therefore made the same cut as Debian 13 and dropped everything below 21. A dnf install java-17-openjdk-headless ends there with No match for argument.
headless or not, JRE or JDK
On a server, always take the -headless variant. It leaves out the graphical libraries and therefore pulls in no X11 dependencies, which saves dozens of pointless packages on a root server. And -jre-headless is enough as long as you only run finished JAR files. Only once you compile yourself, or a tool calls javac, do you need openjdk-21-jdk-headless.
On the Red Hat family it is worth a second look at the dependencies at this point. On AlmaLinux 9, the package java-21-openjdk-devel pulls in a surprisingly long chain of graphical packages, among them webkit2gtk3-jsc, xorg-x11-fonts, xdg-desktop-portal and wireplumber. On a server without a desktop you do not want any of that. So check in advance with dnf install --assumeno what actually comes along, and stay with java-21-openjdk-headless as long as you do not have to compile anything.
Minecraft: game version to Java version
Minecraft is the most common reason anyone puts Java on a server in the first place, and at the same time the area with the strictest requirements. The mapping is unambiguous:
| Minecraft Java Edition | Java required |
|---|---|
| 1.6.1 to 1.11.2 | Java 6 or newer |
| 1.12 to 1.16.5 | Java 8 or newer |
| 1.17 to 1.17.1 | Java 16 or newer |
| 1.18 to 1.20.4 | Java 17 or newer |
| 1.20.5 to 1.21.11 | Java 21 or newer |
| 26.1 and newer | Java 25 or newer |
Two things about this that most tables leave out. First: with 26.1, Minecraft abandoned the old 1.x scheme and moved to a year-based scheme, so 26.1 is the first release of the year 2026. 1.21.11 was the last version that gets by with Java 21.
Second: the "or newer" applies to the vanilla server. As soon as modloaders are involved, it no longer holds reliably. A Forge server for 1.20.1 is built on Java 17, and putting it on Java 21 is one of the most common causes of crashes right at startup, even though the number is "bigger". For modded servers, take exactly the version that the modpack author names.
The practical steps are covered in our guides Installing a Minecraft server on Debian and Starting a Minecraft server automatically.
Other server applications and what they require
Beyond Minecraft, these rules apply as a rough guide:
- Apache Tomcat: 9.0.x runs from Java 8 upwards, 10.1.x requires at least Java 11, 11.0.x requires at least Java 17. All three currently maintained branches run cleanly on Java 17.
- Elasticsearch and OpenSearch: they bring their own JVM. Do not install a system Java for them and do not set a global
JAVA_HOMEthat overrides the bundled JVM. That is a classic source of errors after a "cleanup" of the Java installation. - Jenkins: current versions require at least Java 17 and run on Java 21.
- Keycloak, Kafka, Solr, Nexus: they follow the second to last LTS. Check the release notes of the specific version, these projects raise the minimum regularly.
Not every piece of server software that people associate with Java actually needs it. The TeamSpeak 3 server, for example, is a native binary and works without a JVM.
Running several Java versions in parallel and switching between them
On Debian and Ubuntu you can install as many OpenJDK packages at the same time as you like. Each one lands in its own directory under /usr/lib/jvm/ and they do not get in each other's way:
apt install -y openjdk-21-jre-headless openjdk-25-jre-headless
ls /usr/lib/jvm/
What they do share is exactly one file: /usr/bin/java. It is a symlink managed by the alternatives system. You can list the candidates like this:
update-alternatives --list java
update-alternatives --display java
This spelling, with the name after --list, is specific to Debian; on the Red Hat family it looks different, more on that below. The output of --display is the more useful of the two. It shows not only the paths, but also the mode (auto or manual) and the priority of every entry. Switching works interactively with update-alternatives --config java and a numeric selection, in scripts you are better off pinning it:
update-alternatives --set java /usr/lib/jvm/java-21-openjdk-amd64/bin/java
java -version
Never copy the path from someone else's tutorial, take it from update-alternatives --list java. The path shown above applies to OpenJDK from the distribution package. Anyone following the Temurin section further down does not have that directory at all: there it is called /usr/lib/jvm/temurin-21-jre-amd64/bin/java, and the command aborts with alternative path ... doesn't exist.
If you also compile, javac has to be switched separately. That is easily forgotten and leads to the absurd situation of compiling with Java 25 and starting with Java 21:
update-alternatives --set javac /usr/lib/jvm/java-21-openjdk-amd64/bin/javac
javac -version
The trap that costs you a night
As long as java is in auto mode, the highest priority always wins, and the highest priority belongs to the newest installed version. So if you add openjdk-25 months later because another service needs it, /usr/bin/java silently jumps to 25 during the next package operation. Your Minecraft server, which had been running on 21 until then, starts on a JVM you never selected after the next reboot.
update-alternatives --set switches the entry to manual and freezes it there. That is the real purpose of the command. You get back into automatic mode with:
update-alternatives --auto java
The more robust route for services is still not to rely on /usr/bin/java at all. Put the full path into your systemd unit, then the version choice is fixed per service and independent of any alternatives switch:
ExecStart=/usr/lib/jvm/java-21-openjdk-amd64/bin/java -Xms2G -Xmx4G -jar server.jar nogui
What such a unit looks like in full is described in Creating a systemd service. Incidentally, that same absolute path is also the reason why a service sometimes does not move along after a Java switch: update-alternatives simply does not touch it.
On AlmaLinux, Rocky Linux, RHEL and Oracle Linux the tool is called alternatives, and update-alternatives is only a symlink to it there. It does not work identically, though: this --list takes no argument. A copied update-alternatives --list java only prints the help text there and exits with return code 2. One of these two lines is correct:
alternatives --list | grep java
alternatives --display java
The output of --display differs as well: instead of java - auto mode it reads java - status is auto. On the Red Hat family the paths also carry the full version number, for example /usr/lib/jvm/java-21-openjdk-21.0.11.0.10-1.el9.x86_64, and not the short Debian form java-21-openjdk-amd64. Only AlmaLinux 10 additionally creates a short symlink java-21-openjdk.
And the automatic mode behaves differently there from what you would expect: after installing Java 17 next to an already present Java 21, alternatives on AlmaLinux 9 switched the link /usr/bin/java to 17 on its own, so to the older version. For that reason, set alternatives --set java <path> explicitly after every additional installation and verify with java -version.
When the distribution does not have the version: Temurin
There is a clean answer for the gaps in the table above: Eclipse Temurin from Adoptium supplies temurin-8 through temurin-26 for trixie, bookworm, noble and jammy. That gets you Java 21 on Debian 12 as well, and Java 17 on Debian 13, without third-party PPAs or hand-copied tarballs in /opt.
Note that apt-key is deprecated. The key belongs in /etc/apt/keyrings/ and is referenced via signed-by:
apt install -y wget gnupg ca-certificates apt-transport-https
install -d -m 0755 /etc/apt/keyrings
wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | tee /etc/apt/keyrings/adoptium.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list
apt update
apt install -y temurin-21-jre
Temurin packages register themselves in the alternatives system as well, so they show up in update-alternatives --display java and can be mixed with the distribution's own OpenJDK packages. Their directories live under /usr/lib/jvm/temurin-21-jre-amd64 or under a similarly named path.
Error messages verbatim and what they mean
E: Unable to locate package openjdk-17-jre-headless
That version does not exist in this distribution. On Debian 13 this is the normal case for Java 8, 11 and 17. No typo, no missing apt update: use Temurin or a different version.
java.lang.UnsupportedClassVersionError: ... has been compiled by a more recent version of the Java Runtime (class file version 65.0), this version of the Java Runtime only recognizes class file versions up to 61.0
The classic. The application was built for a newer JVM than the one you started. The numbers translate like this:
| class file version | Java |
|---|---|
| 52.0 | 8 |
| 55.0 | 11 |
| 60.0 | 16 |
| 61.0 | 17 |
| 65.0 | 21 |
| 69.0 | 25 |
So 65.0 against 61.0 means, in plain terms: the software wants Java 21, you are running Java 17. On Minecraft servers from 1.20.5 onwards you often see this error wrapped as Error: LinkageError occurred while loading main class net.minecraft.bundler.Main, with the actual cause on the line below it.
java: command not found
Either no JVM is installed, or you have only unpacked a JDK directory into /opt without registering it. Check ls /usr/lib/jvm/. If there are directories there but /usr/bin/java is missing, register the entry afterwards:
update-alternatives --install /usr/bin/java java /usr/lib/jvm/java-21-openjdk-amd64/bin/java 2111
update-alternatives: error: no alternatives for java
The alternatives system does not know a single candidate. This happens when Java was installed by hand. Same fix as above.
update-alternatives: error: alternative path /usr/lib/jvm/... doesn't exist
You have set a path that has since disappeared, typically after an uninstall. Make sure Debian paths contain the architecture: java-21-openjdk-amd64, not java-21-openjdk.
The service runs, but on the wrong version.
Very likely the systemd unit contains an absolute path, or the unit sets its own JAVA_HOME. Both override update-alternatives without any warning.
The server starts but dies after a few seconds with no comprehensible message.
With Java applications this is often not a version problem but a shortage of memory: the kernel kills the process when -Xmx is set larger than the free RAM. Our articles Setting up swap against out of memory and Disk full on Linux: how to clean up fit here.
How to tell that the right version is really running
The first check is trivial, but do it after every switch anyway:
java -version
The output has to name the expected major version, for example openjdk version "21.0.11". The second check is the more informative one, because it shows the path that is actually resolved and therefore also whether you have just caught Temurin or the distribution's OpenJDK:
readlink -f "$(command -v java)"
Use command -v here deliberately and not which. which is an external program, and in the minimal installation of AlmaLinux 9 and 10, Rocky Linux 9 and Oracle Linux 9 it is not included at all. The variant with which ends there with which: command not found, followed by readlink: missing operand. command -v is a shell builtin and works on all of the distributions named.
And if you want to know what the JVM itself thinks about its home, because an application evaluates JAVA_HOME:
java -XshowSettings:properties -version
In the output, java.home and java.version are the interesting lines. If java.home deviates from what you expect, a JAVA_HOME is set somewhere and it wins.
For a running service, in the end only what the process itself uses counts. You can read that straight out of the process list, which shows the full path of the JVM it was started with:
ps -eo pid,args | grep '[j]ava'
If you see /usr/lib/jvm/java-21-openjdk-amd64/bin/java there, the matter is settled. If you see a bare java, your service hangs on the alternatives symlink and moves with it. For a production service that is the worse of the two variants.
If you are setting up a server from scratch anyway, it is worth reading our checklist for new root servers first: Java belongs at the end of that list, not at the beginning, because troubleshooting a JVM only becomes enjoyable once SSH access, firewall and time zone are working.
In summary
Java 21 is the standard answer today, Java 25 the future-proof one, Java 17 the one winding down, and Java 8 and 11 are migration debt. What decides the matter is not the number alone, but the combination of application and distribution: Debian 12 gives you only 17, Debian 13 only 21 and 25, Ubuntu gives you everything. Where the package is missing, Temurin fills the gap. And as soon as more than one version sits on the machine, the rule is: update-alternatives --set instead of automatic mode, and in systemd units better the absolute path right away.
Frequently asked questions
Which Java version should I install on a new server in 2026?
Why can I not find openjdk-17-jre-headless on Debian 13?
Which Java version does my Minecraft server need?
Can I install several Java versions at the same time?
What does UnsupportedClassVersionError with class file version 65.0 mean?
Why does my service still use the old Java version after switching?
How long will Java 17 be maintained?
2026 KernelHost GmbH. All rights reserved. This guide is protected by copyright. Republishing it on other websites, in whole, in part or in edited form, is not permitted without our written consent. Quoting with a source credit and a link is expressly welcome.

