Install Eclipse Temurin 21 on Ubuntu from Adoptium’s own apt repository. You get the same OpenJDK 21 build that Adoptium ships for every other platform, and apt upgrade keeps it patched with the rest of your system. The tarball route still works, but it leaves you managing paths, updates and trusted certificates by hand, which is why most guides that start there go stale within one release.
This guide adds the Adoptium repository, installs the Temurin 21 JDK (or just the JRE), checks it with a real compile, and sets JAVA_HOME. It also covers the two things that catch people later: installing Temurin 25 next to 21 silently changes your default java, and your company’s root CA only reaches Temurin’s trust store if you installed the package. We ran every command below in October 2026 on Ubuntu 26.04 and 24.04 with Temurin 21.0.12.1.
What you need
Nothing here needs a desktop, and the same commands work on a server image. Before you start, check that your machine has the following:
- Ubuntu 26.04 LTS or 24.04 LTS (we tested on amd64)
- A user with sudo rights
- About 375 MB free for the JDK, or 184 MB for the JRE alone
- Outbound HTTPS to
packages.adoptium.net
If you already have a Java from the Ubuntu archive, leave it in place for now. Temurin sits in its own directory under /usr/lib/jvm, so the two do not overwrite each other, and the switching section below shows you how to pick which one answers to java.
Add the Adoptium apt repository
Adoptium signs its packages with its own key, so fetch that key into a keyring file that only this repository trusts. A key dropped into the global keyring would vouch for packages from every source you have configured, which is a bigger blast radius than a JDK repository deserves. The commands also install gpg, because minimal server images often ship without it and the dearmor step fails silently into an empty file.
sudo apt update
sudo apt install -y wget apt-transport-https gpg
wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | sudo tee /etc/apt/keyrings/adoptium.gpg > /dev/null
Before you trust the key, confirm its fingerprint. A wrong or empty key file only shows up later as a NO_PUBKEY error on apt update, so it is quicker to check now than to debug it then.
gpg --show-keys /etc/apt/keyrings/adoptium.gpg
The key ID ends in 65F8F04B and the user ID names the Eclipse Temurin team. If you see anything else, stop and fetch the key again.
pub rsa2048 2021-11-16 [SC]
3B04D753C9050D9A5D343F39843C48A565F8F04B
uid Adoptium GPG Key (DEB/RPM Signing Key) <[email protected]>
sub rsa2048 2021-11-16 [E]
Now write the repository definition. It reads the release codename from /etc/os-release instead of hardcoding it, so the same command produces resolute on 26.04 and noble on 24.04. Adoptium publishes a tree for both. The resolute tree arrived in May 2026, a few weeks after 26.04 shipped, so you no longer need the old workaround of pointing a new release at the previous codename.
printf 'Types: deb\nURIs: https://packages.adoptium.net/artifactory/deb\nSuites: %s\nComponents: main\nSigned-By: /etc/apt/keyrings/adoptium.gpg\n' "$(. /etc/os-release && echo $VERSION_CODENAME)" | sudo tee /etc/apt/sources.list.d/adoptium.sources
On 26.04 the file comes out as shown below. Adoptium’s own docs still use the older one-line .list format with the key in trusted.gpg.d. That also works, but the .sources file with Signed-By keeps the key scoped to this one repository, so we use it here.
Types: deb
URIs: https://packages.adoptium.net/artifactory/deb
Suites: resolute
Components: main
Signed-By: /etc/apt/keyrings/adoptium.gpg
Refresh the index and list what the repository offers.
sudo apt update
apt-cache search --names-only '^temurin-' | sort
Every supported line is there as a JDK and a JRE package, from 8 up to the newest feature release. Only 8, 11, 17, 21 and 25 are LTS lines. The others lose support after six months, so do not build anything long-lived on them.
temurin-11-jdk - Eclipse Temurin 11 JDK
temurin-11-jre - Eclipse Temurin 11 JRE
temurin-17-jdk - Eclipse Temurin 17 JDK
temurin-17-jre - Eclipse Temurin 17 JRE
temurin-21-jdk - Eclipse Temurin 21 JDK
temurin-21-jre - Eclipse Temurin 21 JRE
temurin-25-jdk - Eclipse Temurin 25 JDK
temurin-25-jre - Eclipse Temurin 25 JRE
temurin-26-jdk - Eclipse Temurin 26 JDK
temurin-26-jre - Eclipse Temurin 26 JRE
temurin-27-jdk - Eclipse Temurin 27 JDK
temurin-27-jre - Eclipse Temurin 27 JRE
temurin-8-jdk - Eclipse Temurin 8 JDK
temurin-8-jre - Eclipse Temurin 8 JRE
For this guide you only need temurin-21-jdk, plus temurin-25-jdk later if you want to try the side-by-side setup.
Install the Temurin 21 JDK
Install the JDK if your machine compiles anything. Besides the runtime, it ships the tools a build needs:
javac, the compilerjarandjarsignerfor packagingjshellfor quick experimentsjcmd,jstackandjmapfor live diagnostics
That makes it the right package for a build agent, a developer laptop or any host that runs Maven or Gradle.
sudo apt-get install -y temurin-21-jdk
On a fresh 26.04 server, that pulled 13 packages and 375 MB, and 24.04 needed 12 packages and 372 MB. The extras are adoptium-ca-certificates, java-common and a handful of X11 and ALSA libraries that AWT links against. Unlike Ubuntu’s own OpenJDK, Temurin has no separate headless package, so those libraries come along even on a server. The install registers every tool with update-alternatives, which you’ll see scroll past:
0 upgraded, 13 newly installed, 0 to remove and 2 not upgraded.
Need to get 180 MB/180 MB of archives.
After this operation, 375 MB of additional disk space will be used.
update-alternatives: using /usr/lib/jvm/temurin-21-jdk-amd64/bin/jar to provide /usr/bin/jar (jar) in auto mode
update-alternatives: using /usr/lib/jvm/temurin-21-jdk-amd64/bin/jarsigner to provide /usr/bin/jarsigner (jarsigner) in auto mode
update-alternatives: using /usr/lib/jvm/temurin-21-jdk-amd64/bin/java to provide /usr/bin/java (java) in auto mode
update-alternatives: using /usr/lib/jvm/temurin-21-jdk-amd64/bin/javac to provide /usr/bin/javac (javac) in auto mode
A host that only runs a packaged application, such as Tomcat or a Spring Boot jar, does not need the compiler. On a fresh 26.04 server the JRE pulled 18 packages and 184 MB, about half the JDK, so install it there instead. It also keeps javac off production machines, which means nobody can quietly patch and rebuild code on a live server.
sudo apt-get install -y temurin-21-jre
The JRE lands in /usr/lib/jvm/temurin-21-jre-amd64, so on a JRE-only machine use that path wherever this guide says temurin-21-jdk-amd64, and skip the javac and compile checks. Don’t install the JRE next to the JDK either: it registers at priority 2112, one above the JDK’s 2111, so in auto mode the JRE’s java would win, and update-java-alternatives --list shows both.
Verify Temurin 21
Check the runtime and the compiler, then make sure the java on your PATH resolves into the Temurin directory. If an older JDK was installed first, it may still own the command.
java -version
javac -version
readlink -f "$(which java)"
The runtime line names Temurin and the build ends in LTS. If it says Ubuntu instead, another JDK still owns the java alternative, and the switching section fixes that.
openjdk version "21.0.12.1" 2026-08-18 LTS
OpenJDK Runtime Environment Temurin-21.0.12.1+1 (build 21.0.12.1+1-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.12.1+1 (build 21.0.12.1+1-LTS, mixed mode, sharing)
javac 21.0.12.1
/usr/lib/jvm/temurin-21-jdk-amd64/bin/java
A version string only proves that the binary starts, so compile something. Create a small class that prints which vendor built the JVM it runs on, because that one property tells you at a glance whether a script picked up Temurin or some other JDK. Run it as your normal user, since nothing here needs root.
mkdir -p ~/jtest && cd ~/jtest
vim Hello.java
Paste this into the file:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello from " + System.getProperty("java.vendor") + " " + System.getProperty("java.version"));
}
}
Java 21 can run a single source file directly, which is handy for scripts. Try that first, then compile the class the classic way so you know javac works too.
java Hello.java
javac Hello.java && java Hello
Both runs report Eclipse Adoptium as the vendor, which confirms you’re on Temurin and not a JDK left over from an earlier install.
Hello from Eclipse Adoptium 21.0.12.1
Hello from Eclipse Adoptium 21.0.12.1
If javac is missing or the vendor line names another vendor, the alternatives are pointing somewhere else, which the next section fixes.
Keep Java 21 as the default next to Temurin 25
Installing a second Temurin line changes your default java without asking. Each package registers its tools at a priority built from its version, 2111 for Temurin 21 and 2511 for Temurin 25, and in auto mode the highest number wins. We installed temurin-25-jdk on a box that was running 21 and checked straight away:
sudo apt-get install -y temurin-25-jdk
java -version 2>&1 | head -1
The default had already moved to 25. Any service that calls plain java would come back on the new runtime after its next restart, so an application that was only certified on 21 changes Java versions during an unrelated package install. You’d usually find out from a stack trace, not from apt.
openjdk version "25.0.4.1" 2026-08-18 LTS
Temurin packages drop a .jinfo file in /usr/lib/jvm, so Debian’s update-java-alternatives tool knows about them. You can list every installed JDK with it.
update-java-alternatives --list
Each line shows the name to pass to --set, the priority and the install path. The priority column explains the jump to 25.
temurin-21-jdk-amd64 2111 /usr/lib/jvm/temurin-21-jdk-amd64
temurin-25-jdk-amd64 2511 /usr/lib/jvm/temurin-25-jdk-amd64
Switch everything back to 21 in one command. Unlike update-alternatives --config java, this moves javac, jar and the other tools together, so you never compile with one JDK and run with another:
sudo update-java-alternatives --set temurin-21-jdk-amd64
java -version 2>&1 | head -1
javac -version
Both commands report 21 again:
openjdk version "21.0.12.1" 2026-08-18 LTS
javac 21.0.12.1
The alternatives now sit in manual mode, so a later Temurin 25 update won’t flip them back. The terminal below shows the whole switch in one place.

Both JDKs are listed with their priorities, then the --set, and the runtime and a source-file run confirm Temurin 21.
Ubuntu’s own OpenJDK 21 registers at the same 2111 as Temurin 21. When we installed openjdk-21-jdk-headless on this box after the switch above, java stayed on Temurin because the alternatives were already in manual mode. With two JDKs of the same version on one host, set the default explicitly rather than trusting auto mode to break the tie. The guide to setting the default Java version covers the per-tool view in more depth.
Set JAVA_HOME
Maven, Gradle, Tomcat and most launch scripts read JAVA_HOME rather than following /usr/bin/java. That means switching alternatives alone does not change which JDK they build with. Set it system-wide in a profile snippet, so every login shell on the machine gets the same value.
echo 'export JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64' | sudo tee /etc/profile.d/java-home.sh
Open a new login shell, or run bash -l, and check it.
bash -lc 'echo $JAVA_HOME'
The path has no patch version in it, so you never have to edit it after a Temurin 21 update. That is the main advantage over a tarball under /opt, where the directory name changes with every release unless you add a symlink.
/usr/lib/jvm/temurin-21-jdk-amd64
A systemd service does not read /etc/profile.d, so the snippet has no effect on anything started at boot. Put Environment=JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64 in the service’s unit file instead, and point ExecStart at the full path to java if the service must never follow the system default. The guide to running a jar with systemd shows a complete unit.
Trust your company’s root CA
Java keeps its own trust store instead of reading /etc/ssl/certs, which is why internal HTTPS calls fail with PKIX path building failed on machines where curl works fine. The Temurin packages handle this for you: cacerts is a symlink to /etc/ssl/certs/adoptium/cacerts, and adoptium-ca-certificates installs a hook that rebuilds it every time update-ca-certificates runs.
We tested that with a throwaway root CA. Count the trusted entries first, then copy your CA’s PEM file in with a .crt extension and rebuild the bundle.
keytool -list -cacerts -storepass changeit 2>/dev/null | grep -c trustedCertEntry
sudo cp example-corp-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
The last lines of the output show the Adoptium hook running right after the system bundle is rebuilt.
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
/etc/ssl/certs/adoptium/cacerts successfully populated.
done.
Java now trusts the new CA without a single keytool -importcert command. Count the entries again and search for your CA; the alias is its common name in lower case with the spaces removed, so swap examplecorp for part of your own CA’s name.
keytool -list -cacerts -storepass changeit 2>/dev/null | grep -c trustedCertEntry
keytool -list -cacerts -storepass changeit 2>/dev/null | grep -i examplecorp
The count went from 121 to 122, and our test CA, Example Corp Root CA, shows up as examplecorprootca:
122
examplecorprootca, Oct 18, 2026, trustedCertEntry,
Removing the file and running sudo update-ca-certificates --fresh took the count back to 121, so retiring an internal CA works the same way. Because the hook runs on every rebuild, configuration management that already pushes CAs to /usr/local/share/ca-certificates covers Java with no extra step. That sync only exists for the apt packages, which is the strongest reason to skip the tarball on any machine that talks to internal services.
Install Temurin 21 from the tarball
The tarball is still useful on a machine where you can’t add a repository, or when an application needs a private JDK in its own directory. Adoptium’s API serves the newest Temurin 21 build at a fixed URL, so the same command keeps working after every patch release.
cd /tmp
curl -sLo temurin21.tar.gz "https://api.adoptium.net/v3/binary/latest/21/ga/linux/x64/jdk/hotspot/normal/eclipse"
The same API publishes the SHA-256 for that build. Check your download against it before you extract anything.
SUM=$(curl -s "https://api.adoptium.net/v3/assets/latest/21/hotspot?os=linux&architecture=x64&image_type=jdk" | python3 -c "import json,sys; print(json.load(sys.stdin)[0]['binary']['package']['checksum'])")
echo "${SUM} temurin21.tar.gz" | sha256sum -c
Anything other than OK means a corrupt or tampered file, so delete it and download again.
temurin21.tar.gz: OK
Extract it under /opt/java and point a version-free symlink at it. Your scripts then reference /opt/java/temurin-21, and a future update only moves the symlink.
sudo mkdir -p /opt/java
sudo tar -xzf temurin21.tar.gz -C /opt/java
JDIR=$(ls -dv /opt/java/jdk-21* | tail -1)
sudo ln -sfn "$JDIR" /opt/java/temurin-21
/opt/java/temurin-21/bin/java -version
It is the identical build to the apt package, down to the build number.
openjdk version "21.0.12.1" 2026-08-18 LTS
OpenJDK Runtime Environment Temurin-21.0.12.1+1 (build 21.0.12.1+1-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.12.1+1 (build 21.0.12.1+1-LTS, mixed mode, sharing)
To make it the system java, register it with the alternatives system and then select it. Registering alone isn’t enough on a machine that also has packaged JDKs: they sit at priorities 2111 and above, and if you used update-java-alternatives --set earlier the links are in manual mode anyway, so the new entry is ignored until you pick it.
sudo update-alternatives --install /usr/bin/java java /opt/java/temurin-21/bin/java 1000
sudo update-alternatives --install /usr/bin/javac javac /opt/java/temurin-21/bin/javac 1000
sudo update-alternatives --set java /opt/java/temurin-21/bin/java
sudo update-alternatives --set javac /opt/java/temurin-21/bin/javac
readlink -f /usr/bin/java /usr/bin/javac
On our test box /usr/bin/java still pointed at the packaged JDK after the two --install lines. After the --set lines both links resolve into /opt/java:
update-alternatives: using /opt/java/temurin-21/bin/java to provide /usr/bin/java (java) in manual mode
update-alternatives: using /opt/java/temurin-21/bin/javac to provide /usr/bin/javac (javac) in manual mode
/opt/java/jdk-21.0.12.1+1/bin/java
/opt/java/jdk-21.0.12.1+1/bin/javac
The tarball behaves differently from the package in ways that are easy to miss until something fails:
lib/security/cacertsis a plain file with 118 entries, not a link to the system storeupdate-ca-certificatesnever touches it, so internal CAs needkeytool -importcert- nothing patches it, so each Temurin 21 update means a new download and a moved symlink
That is fine for a pinned application runtime that you rebuild anyway. For a general-purpose Java on a server, the package does the same job with less work.
Remove Temurin
Every Temurin package depends on adoptium-ca-certificates, so purging it removes all of them, including a Temurin 25 or a JRE you kept next to 21. The quoted wildcard makes that explicit, and the autoremove clears the libraries they pulled in.
sudo apt-get purge -y 'temurin-*' adoptium-ca-certificates
sudo apt-get autoremove --purge -y
With the 21 JDK, 21 JRE and 25 JDK installed, the purge removed four packages and freed 844 MB, and the autoremove another 18.2 MB. If you only want to drop one line, purge just that package, such as temurin-25-jdk, and leave adoptium-ca-certificates alone. Remove the repository, key and profile snippet too, because apt otherwise keeps polling Adoptium on every update and a stale JAVA_HOME points the next login shell at a directory that no longer exists.
sudo rm -f /etc/apt/sources.list.d/adoptium.sources /etc/apt/keyrings/adoptium.gpg /etc/profile.d/java-home.sh
For a tarball install, drop the alternatives first and the directory second. Deleting the files first leaves /usr/bin/java pointing at nothing, and you then have to clean up the dangling links by hand.
sudo update-alternatives --remove java /opt/java/temurin-21/bin/java
sudo update-alternatives --remove javac /opt/java/temurin-21/bin/javac
sudo rm -rf /opt/java
Removing a manually selected alternative switches the links back to auto mode, and auto mode picks the highest priority. On our box that was Temurin 25, not 21, so run sudo update-java-alternatives --set temurin-21-jdk-amd64 again afterwards if 21 should stay the default.
Temurin 21 vs Ubuntu’s OpenJDK 21
Both are built from the same upstream OpenJDK 21 release and were at the same patch level in our test, although Ubuntu adds its own distro patches and each is built with its own toolchain. They differ in who ships the updates and what comes along with the package. We installed both on the same 26.04 server and compared them.
| Item | Eclipse Temurin 21 (Adoptium repo) | OpenJDK 21 (Ubuntu archive) |
|---|---|---|
| Package | temurin-21-jdk, temurin-21-jre | openjdk-21-jdk, plus -headless variants |
| Install path | /usr/lib/jvm/temurin-21-jdk-amd64 | /usr/lib/jvm/java-21-openjdk-amd64 |
| Vendor string | Eclipse Adoptium | Ubuntu |
| Alternatives priority | 2111 | 2111 |
| Disk | 375 MB (JDK) and 184 MB (JRE) on a fresh server | 302 MB on 26.04, 305 MB on 24.04 (headless JDK, installed next to Temurin) |
| Headless package | No, X11 libraries always come along | Yes |
| Trust store | /etc/ssl/certs/adoptium/cacerts, rebuilt by the adoptium-cacerts hook | /etc/ssl/certs/java/cacerts, rebuilt by the jks-keystore hook |
| Same build on other OSes | Yes: RHEL family, macOS, Windows, containers | Ubuntu only |
| Updates arrive | When Adoptium publishes, through its repo | Through the Ubuntu security pocket |
Pick Temurin when the same JDK has to run on Ubuntu, Rocky, macOS and your eclipse-temurin container images, because the build string then matches everywhere and a bug report from one platform applies to the others. A single-distro server fleet is better served by Ubuntu’s package, covered in the Java 21 on Ubuntu guide, since its fixes land with the rest of your security updates. For the other platforms, see Temurin 21 on Debian and Temurin on RHEL-family systems, and once Java is in place, Jenkins on Ubuntu is a common next install.