updatejdk.sh lists, installs, updates and downloads JDKs and JREs on macOS and Linux: Eclipse Temurin by default, other distributions like Zulu, Corretto or Liberica on request, and any other .tar.gz or .tar.xz from a URL. It can also just download the packages for other operating systems and architectures, e.g. to bundle them with an application.
The binaries are taken from the Adoptium API or the foojay Disco API, verified by their checksum, and unpacked into the system's JVM directory. No package manager is needed, and you can keep several feature versions and distributions side by side and bring them all up to date with one command.
bash,curl,jq,tarjqcomes with macOS from version 15 on. If it is not installed, the script downloads jq 1.8.2 from GitHub for each run, verified by its SHA-256, on macOS and Linux for x64 and aarch64.shasum(macOS) orsha256sumandsha1sum(Linux)xzon Linux, only to install a.tar.xzlike Red Hat's builds with GNU tar- root privileges to write to the JVM directory, either by running as root or via
sudo, ordoasif there is nosudo, as on many Alpine systems
| Platform | Architectures | JVM directory |
|---|---|---|
| macOS | x64, aarch64 (Apple Silicon) | /Library/Java/JavaVirtualMachines |
| Linux (glibc) | x64, aarch64, arm, ppc64le, s390x, riscv64 | /usr/lib/jvm |
| Linux (musl, e.g. Alpine) | x64, aarch64 | /usr/lib/jvm |
Which feature versions exist for an architecture depends on what the distribution offers. -l shows it.
On Linux, install the requirements by
sudo apt install curl jqon Debian and Ubuntu, wherecurlis not installed by default,sudo dnf install curl jqon RHEL, Rocky Linux and Fedora,apk add bash curl jqon Alpine.
jq may be left out, the script downloads it if it is missing (see above). If a command is missing, the script names the command that installs it.
updatejdk.sh is a single file. Download it and make it executable:
curl -fsSL -o updatejdk.sh https://bit.ly/updatejdk
chmod +x updatejdk.shReport bugs and wishes as issues.
updatejdk.sh -l|--list [<variant>|<architecture>]
[-a|--api [<distribution>]]
updatejdk.sh -i|--install <variant> [-a|--api <distribution>]
[<options>]
updatejdk.sh -i|--install <variant> -s|--source <url>
[-S|--sha256 <hash>] [<options>]
updatejdk.sh -u|--update [<variant>] [-a|--api <distribution>]
[<options>]
updatejdk.sh -g|--get <variant> [-a|--api <distribution>]
[-o|--os <os>] [-p|--package <format>] [-d <path>]
updatejdk.sh -h|--help
updatejdk.sh -v|--version
updatejdk.sh -C|--copyright
| Action | Description |
|---|---|
-l, --list [<variant>|<architecture>] |
list all variants that are available for this platform, or for another architecture, or only the given variant, of both types unless one is given; installed ones are marked with * and shown with their path; without -a, also those of the other distributions installed, each in a table of its own, like -u updates them |
-i, --install <variant> |
install a variant, or update it if it is installed already |
-u, --update [<variant>] |
update an installed variant, or all installed variants if <variant> is omitted |
-g, --get <variant> |
download the package of a variant and its checksum, but do not install it, see Downloading packages with -g |
| Option | Description |
|---|---|
-a, --api <distribution> |
temurin or adoptium (the default), or a distribution of the foojay Disco API, see Other distributions. -l -a without a distribution lists all of them, -l x64 -a only those for x64 |
-C, --copyright |
print the copyright and the license and exit |
-d, --dir <path> |
use another JVM directory instead of the default, with -g the directory to save the package to |
-f, --force |
download again, and with -i or -u install again, even if the variant is up to date |
-h, --help |
print the help and exit |
-k, --keep |
with -i or -u, keep the downloaded archive in the current directory, its path is printed at the end |
-L, --link [<path>] |
with -i or -u, create or replace a relative symbolic link to the installed variant, see A stable path with -L |
-n, --dry-run |
with -i, -u or -g, look up the releases and show what would be done, but do not change anything |
-o, --os <os> |
with -l or -g, the operating system: mac, linux, alpine-linux or windows, defaults to this one |
-O, --oracle-vendor |
with -z, start the tzupdater with java.vendor "Oracle Corporation", for tzupdaters that refuse a Java of another vendor |
-p, --package <format> |
with -l or -g, the format of the package like tar.gz, zip, pkg, dmg or msi, defaults to zip on Windows and tar.gz otherwise |
-s, --source <url> |
with -i, install the .tar.gz or .tar.xz at <url>, see Any tarball with -s |
-S, --sha256 <hash> |
with -s, the SHA-256 of the archive |
-t, --test |
with -i or -u, run java -Xmx10m -version of the variants concerned, even if they were up to date; the script fails if it does not run |
-v, --version |
print the version, the copyright and the license and exit |
-z, --tzupdater [<jar>] |
with -i or -u, update the time zone data of the installed variants by Oracle's tzupdater.jar, see Time zone data |
-Z, --tzdata <url> |
with -z, take the time zone data from <url> rather than from IANA |
A <variant> is <feature-version>[:jdk|:jre][:<architecture>]. The type defaults to jdk, the architecture to the one of this machine, see Other architectures. The <feature-version> is either:
- a number like
21, ltsfor the most recent LTS release, orlatestfor the most recent feature release.
updatejdk.sh -l # what is available, what is installed?
updatejdk.sh -l 21 # the same for Java 21 only, JDK and JRE
updatejdk.sh -l lts:jre:x64 # the JRE of the most recent LTS for x64
updatejdk.sh -l -a # which distributions are there?
updatejdk.sh -l -a zulu # what is available of Azul Zulu?
updatejdk.sh -l -d ~/jvm # everything installed in ~/jvm, by distribution
updatejdk.sh -i lts # install the most recent LTS JDK
updatejdk.sh -i 21:jre # install the Java 21 JRE
updatejdk.sh -i 21 -a corretto # install the Amazon Corretto 21 JDK
updatejdk.sh -i 21 -k # install the Java 21 JDK and keep the archive
updatejdk.sh -u -n # which updates are there?
updatejdk.sh -u # update everything that is installed
updatejdk.sh -u -t # ... and check that every java runs
updatejdk.sh -u 21 # update the Temurin 21 JDK only
updatejdk.sh -u -a zulu # update the Zulu installations only
updatejdk.sh -d ~/jvm -i 25 # install into a directory of your own, no root needed
updatejdk.sh -d ~/jvm -i lts -L ~/jvm # ... and point ~/jvm/latest to it
updatejdk.sh -i 25 -s https://download.oracle.com/java/25/latest/jdk-25_macos-aarch64_bin.tar.gz
updatejdk.sh -d build/jre -i 21:jre:x64 # the x64 JRE, e.g. for a universal macOS app
updatejdk.sh -u -z ~/Downloads/tzupdater.jar # update everything, including the time zone dataSample output of -l:
Asking https://api.adoptium.net/v3 for Temurin releases for macOS aarch64 ...
VERSION TYPE LATEST FORMATS INSTALLED
--------- ----- -------------------- ---------------- ---------
* 21 LTS jdk jdk-21.0.13+9 tar.gz pkg 21.0.13+9-LTS (up to date) /Library/Java/JavaVirtualMachines/temurin-21.jdk
21 LTS jre jdk-21.0.13+9 tar.gz pkg
25 LTS jdk jdk-25.0.5+11 tar.gz pkg
...
Asking https://api.foojay.io/disco/v3.0 for zulu releases for macOS aarch64 ...
VERSION TYPE LATEST FORMATS INSTALLED
--------- ----- -------------------- ---------------- ---------
...
* 21 LTS jdk 21.0.12.1+1 dmg tar.gz zip 21.0.12.1+1-LTS (up to date) /Library/Java/JavaVirtualMachines/zulu-21.jdk
21 LTS jre 21.0.12.1+1 dmg tar.gz zip
...
* = installed in /Library/Java/JavaVirtualMachines
Without -a, -l lists Temurin and every other distribution that is installed in the directory, each in a table of its own, just as -u updates them all.
- It looks up the latest release of the variant through the API. If the installed release is the same, it stops here (unless
-fis given). With-n, it shows what it would download and install, and stops as well. - It checks the privileges for the JVM directory before downloading. If root is needed,
sudo(ordoas) asks for the password right away. Without either, the script stops with a hint to run it as root.doasasks again for each step unlesspersistis set indoas.conf. - It downloads the archive, verifies its checksum and size, refuses archives with absolute paths or
.., and unpacks it. - It moves the old installation aside, puts the new one in place, and removes the old one only after that succeeded.
- With
-t, it runsjava -Xmx10m -versionof the installation.
Installations are named:
<distribution>-<version>.<type>on macOS, e.g.temurin-21.jdk,zulu-21.jre<distribution>-<version>-<type>on Linux, e.g.temurin-21-jdk,corretto-21-jdk
Where each installation is from is recorded in .updatejdk/<name> in the JVM directory. The script only ever replaces a directory that contains a Temurin according to its release file, or that has such a record.
With -a <distribution>, -l, -i and -u use the foojay Disco API instead of Adoptium. It knows about 30 distributions, among them zulu, corretto, liberica, oracle, sap_machine, microsoft, graalvm_community, semeru and dragonwell. -l -a without a name lists all of them, with a note on those that are no longer maintained. Given a <variant> or <architecture>, --os or --package, it lists only the distributions that have packages for it, e.g. -l x64 -a for this operating system on x64, -l 21:jre -a --os windows or -l -a --os linux --package tar.xz. -a temurin and -a adoptium always use the Adoptium API.
-u without a <variant> updates all installations, each from its own source. With -a, it updates those of that distribution only. -u <variant> updates the variant of the distribution given by -a, Temurin by default.
The script requires a checksum, but not every distribution publishes one in the same way:
| Distribution | Checksum |
|---|---|
| Temurin | SHA-256 by the Adoptium API |
| Zulu | SHA-256 by the foojay Disco API |
| Oracle, Microsoft, SapMachine | SHA-256 from the file the foojay Disco API points to, or the .sha256(.txt) next to the archive |
| Corretto | SHA-256 by Corretto's own latest_sha256 endpoint. As this covers the latest release only, the script downloads Corretto's latest release, which may be newer than the one foojay knows |
| Liberica | SHA-1 only, the script says so |
Without any checksum, the script refuses to install.
An installation is made of the .tar.gz of a distribution, or of its .tar.xz if there is no .tar.gz, like for Red Hat's builds for Linux. To unpack a .tar.xz, GNU tar needs the xz command, the script checks for it before downloading. BusyBox and macOS tar unpack it by themselves.
-s <url> installs any .tar.gz or .tar.xz that contains a JDK or JRE, e.g. from jdk.java.net or Oracle:
- The SHA-256 is taken from
<url>.sha256,<url>.sha256.txtor the.sha256.txtwithout.tar.gz. If there is none, pass it by-S,--sha256 <hash>. - The
<variant>must give the feature version as a number, e.g.-i 25or-i 25:jre. After unpacking, the script checks that the archive contains exactly that, according to itsreleasefile and whether it has ajavac. - The installation is named
custom-<version>-<type>(custom-<version>.<type>on macOS). - Running the same command again does nothing if the checksum has not changed.
-uskips these installations, because a URL does not tell whether there is something newer.
A variant can name another architecture than the one of this machine, e.g. to bundle the JREs for both Intel and Apple Silicon Macs with a universal app:
updatejdk.sh -d build/jre -i 21:jre:aarch64 # -> build/jre/temurin-21.jre on Apple Silicon
updatejdk.sh -d build/jre -i 21:jre:x64 # -> build/jre/temurin-21-x64.jre
updatejdk.sh -d build/jre -u # updates both later<architecture>is one ofx64,aarch64,arm,ppc64le,s390xandriscv64.x86_64andamd64are accepted forx64,arm64foraarch64. The type may be omitted, as in21:x64.- Only installations for another architecture have it in their name, e.g.
temurin-21-x64.jreorzulu-21-aarch64-jdk. Those of this machine's architecture keep their usual name. - Another architecture can only be installed into a directory of your own by
-d, so that it does not get in the way ofjava_homeorupdate-alternatives. It is not registered withupdate-alternativeseither. - After unpacking, the script checks that the archive is for the requested architecture, according to the
OS_ARCHof itsreleasefile. This also applies to-s. -zand-tneed to run thejavaof the installation. If it does not run on this machine, e.g. an x64 Java on Apple Silicon without Rosetta 2, the script skips both for it with a warning.-l x64shows what is available for x64 and marks the x64 installations.-l 21:x64or-l 21:jre:x64narrows it down to a variant.- With
-uand no<variant>,-Lprefers this machine's architecture.
-g <variant> downloads the package of a variant, verifies it like an installation, and saves it with its checksum to the current directory, or to the one given by -d. Nothing is installed, so no root privileges are needed, and any operating system and architecture can be chosen, e.g. to bundle JREs with an application:
updatejdk.sh -g 21:jre:x64 --os windows # OpenJDK21U-jre_x64_windows_hotspot_….zip
updatejdk.sh -g 21:jre:x64 --os windows --package msi # the installer instead
updatejdk.sh -g 21:jre --package pkg -d ~/Downloads # the macOS installer
updatejdk.sh -g 21:jre:x64 --os linux -a zulu # Zulu for Linux x64--osismac,linux,alpine-linuxorwindows, this machine's by default.--packagedefaults to the archive of the operating system:zipon Windows,tar.gzotherwise. Which formats there are,-lshows in itsFORMATScolumn, also for another operating system, e.g.-l 21 --os windows. With--package,-llists only the variants that come in it. Temurin offers the archive and an installer (pkgon macOS,msion Windows), foojay distributions may offer more, likedmg,deborrpm.- Next to the package, the script writes
<package>.sha256(.sha1for Liberica) in the format ofshasumandsha256sum, so you can check it later byshasum -a 256 -c <package>.sha256. - If the package is there already with the right checksum, it is not downloaded again, unless
-fis given.-nshows what would be downloaded.
macOS
export JAVA_HOME=$(/usr/libexec/java_home -v 21)Linux
export JAVA_HOME=/usr/lib/jvm/temurin-21-jdkOn Linux, installations in the default directory /usr/lib/jvm are also registered with update-alternatives (alternatives on RHEL) for java, and for javac if it is a JDK.
- The links the distribution attaches to these groups (
keytool,jexec, man pages, …) are registered as well, as far as the installation provides them. The script prints the ones it can't provide. - The priority equals the feature version (e.g. 21). This is deliberately low, so the distribution's default stays selected in auto mode. To use an installation system-wide, select it explicitly:
sudo update-alternatives --config javaWith -L [<path>], the script creates a symbolic link after -i or -u, so that JAVA_HOME can stay the same across updates:
- If
<path>is a directory, or the directory given by-deven if it does not exist yet, the link is<path>/latest. Otherwise<path>is the name of the link itself, e.g.-L ~/jvm/java21. - Without
<path>, the link islatestin the current directory. - An existing symbolic link is replaced. Anything else that exists at that path is left alone, and the script stops before downloading.
- The link points to the Java home of the variant: the installation directory on Linux, its
Contents/Homeon macOS. It is relative, e.g.latest -> temurin-21.jdk/Contents/Home, so it stays valid if the link and the installation are moved together. - With
-uand no<variant>, it points to the highest feature version that was updated: Temurin before other distributions, the JDK before the JRE. - The link is set even if the variant was up to date already.
updatejdk.sh -i lts -L ~/jvm
export JAVA_HOME=~/jvm/latest
cd ~/jvm && updatejdk.sh -i lts -L # the same, from within ~/jvmA Java release ships the time zone data that was current when it was built. To get newer data without waiting for the next release, pass Oracle's tzupdater with -z:
- Download it from Oracle. Its license does not allow redistribution, so the script does not download it itself.
-zwithout a path usestzupdater.jarnext to the script.- After each
-ior-u, the script runsjava -jar tzupdater.jar -lwith thejavaof every variant concerned, even if the variant was up to date already. This fetches the latest release from IANA, so access to iana.org is needed. - With
-Z <url>, the tzupdater takes the data from<url>instead, e.g. a mirror or afile://URL of atzdata*.tar.gz. - With
-O, the script starts the tzupdater with-Djava.vendor="Oracle Corporation", for tzupdater versions that refuse a Java of another vendor. - An update replaces
lib/tzdb.datwith the one of the new release, so pass-zon every-u.
On Linux, the script refuses to touch a directory that belongs to a system package (checked by dpkg -S or rpm -qf).
- The Temurin RPM installs to
/usr/lib/jvm/java-21-temurin-jdkand adds a symlinktemurin-21-jdk, which is exactly the script's own name for that version.-lshows such variants asmanaged by package <name>. -irefuses to replace them.-uskips them, because they are updated bydnf/apt.
- macOS (Apple Silicon), including Zulu, Corretto, Liberica, SapMachine and Oracle by
-s - macOS 15.8.1 Sequoia (Intel, x64), including Zulu, Corretto, Liberica and Oracle
- Ubuntu 26.04 LTS, x64, including
update-alternatives, Zulu, Corretto, Liberica, SapMachine, Microsoft, Red Hat and Oracle by-s - Ubuntu 26.04.1 LTS, aarch64, including Zulu, Corretto, Liberica and Oracle
- Rocky Linux, x64
- Alpine Linux, x64
On Rocky Linux and Alpine Linux, only Temurin has been tested.
See RELEASE_NOTES.md.
Copyright (c) 2026 Johann N. Löfflmann, https://johann.loefflmann.net
Licensed under the Apache License 2.0. -C prints the copyright and the license notice.
updatejdk is an independent project. It is not affiliated with, endorsed or sponsored by Oracle, the Eclipse Foundation or any vendor of the distributions it installs. It only downloads their published builds.
Oracle, Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates. Eclipse Temurin and Adoptium are trademarks of the Eclipse Foundation. Other names may be trademarks of their respective owners.