actions/setup-java v6: Installing a JDK Inside a GitHub Actions Workflow
Set up your GitHub Actions workflow with a specific version of Java.
At a glance
- What is it?
- The action downloads a chosen Java distribution, puts it on PATH, sets JAVA_HOME, and can cache Maven, Gradle and sbt dependencies. v6 is a breaking release for anyone still on v1 through v4, and the migration is mostly about renamed inputs.
- Who is it for?
- Adopt actions/setup-java if your build runs on GitHub-hosted or self-hosted runners and you want the JDK pinned by a workflow file rather than by a machine image. Do not adopt it if you build Java outside GitHub Actions, or if you depend on AdoptOpenJDK identifiers, which v6 removed.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem setup-java solves on a runner
A GitHub-hosted runner image ships with some JDK already installed. That is fine until a build needs a specific distribution at a specific version, and the image changes underneath you. Pinning the JDK in the workflow file removes that dependency on the image. The action is aimed at people who already run Java, Scala or Kotlin builds in GitHub Actions and want the toolchain declared in YAML rather than inherited from whatever the runner happens to carry.
The README lists what the action does in one place: it downloads and installs Java from a supported distribution, resolves the version from a request, a version file, or the latest stable release alias, adds the result to PATH, configures JAVA_HOME, and optionally caches build dependencies for Maven, Gradle and sbt. It also registers problem matchers for compiler diagnostics and uncaught exceptions, which is the part people notice only when it stops working.
It is not a general Java installer. There is no macOS or Windows desktop story here, and nothing in the repository addresses local development. If you arrived looking for how to set up Java on Windows 11 or in VS Code, this action is the wrong tool; it exists for CI jobs.
How the action resolves a distribution and a version
The action takes two required coordinates: distribution and either java-version or java-version-file. The distribution selects a vendor build, for example temurin or microsoft. The version string is not a plain number. The README documents support for JEP 322 multi-field versions such as 18.0.1.1, and v6 added java-version: latest, which resolves the newest stable GA release from the distribution's remote metadata.
That resolution step is the interesting part. Because the version is resolved against vendor metadata, a warm job with a cached JDK can reuse cached release metadata and avoid calling the vendor API, with a fallback to stale metadata when a vendor is down or rate limiting. That is a deliberate trade: fewer network calls, at the cost of a window where the metadata is not current. The README does not state how long cached metadata stays valid, so treat the staleness window as undocumented.
Downloads are verified. According to the README, JDK downloads automatically verify authoritative checksums when a distribution publishes them, and package signature verification defaults to enabled for Temurin and Microsoft builds. The action also caches downloaded JDK installations, and cache-jdk can turn that on or off independently of dependency caching. force-download: true bypasses the tool cache entirely for a reproducible fresh install, which is the escape hatch when a cached JDK is suspected of being wrong.
Installing the action and running a first build
There is nothing to install locally. The action is consumed by reference from a workflow file, and the README's first example is the shortest path to a working job. It checks out the repository, installs Temurin 25, and prints the version.
steps:
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
distribution: temurin
java-version: '25'
- run: java --versionAfter the setup step, the run step should print the Temurin 25 version banner. If it prints a different vendor or version, the setup step did not take effect and the later step is picking up the runner's default JDK.
The second pattern moves the version out of the workflow and into a file the project already owns. The README documents .java-version, .tool-versions and .sdkmanrc as supported version files, and notes that a .sdkmanrc file can also supply the distribution.
steps:
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
distribution: temurin
java-version-file: .java-version
- run: java --versionUsing a version file means the same declaration serves local tooling and CI, which is the main reason to prefer it over a hardcoded string. The cost is that a malformed or missing file fails the job instead of falling back to a default.
Caching dependencies and caching the JDK are separate decisions
Setting cache to maven, gradle or sbt turns on dependency caching. v6 changed the cache keys so they include .mvn/extensions.xml and gradle.properties, which prevents a stale restore when Maven extensions or Gradle dependency properties change. That is a fix for a class of bug that is genuinely hard to debug: a green build locally, a red build in CI, and no obvious cause.
Two inputs give finer control. cache-path adds custom paths to the cached set, and cache-read-only: true restores without saving, which is what you want on a branch where writing to the cache would pollute the main branch's entries. The action also exposes a cache-primary-key output.
JDK caching is now automatic when cache is set, and cache-jdk enables or disables it independently. The trade-off is real: caching a JDK saves download time, but it also means the installed JDK may come from cache rather than a fresh download. force-download: true is the documented way to opt out per run. The README does not describe cache eviction behavior, so a team that needs guaranteed fresh installs should set force-download rather than rely on the cache expiring.
Multiple JDKs, toolchains and what set-default changes
The action can be invoked more than once in a job to install several JDKs. v5 added set-default: false, which installs a JDK without changing JAVA_HOME or PATH. That combination is how you build a matrix of JDKs without the last install silently winning.
Maven toolchains are managed by the action, and the README notes that toolchain entries are preserved across repeated invocations. v6 also added a targeted error when the number of Maven toolchain IDs does not match, which turns a confusing Maven failure into an action failure. That is a small change with outsized effect on debugging time.
For GraalVM distributions, the action sets GRAALVM_HOME in addition to JAVA_HOME. That matters for native-image builds, which look for the former. If you use GraalVM and only check JAVA_HOME, you are checking the wrong variable.
Publishing, signing and the v6 input renames
The action can generate Maven publishing configuration: settings.xml, toolchain entries, GPG signing inputs, and environment-variable based credentials. v6 supports multiple server credentials and custom dependency-resolution repositories, and imports Maven signing keys into an isolated temporary GPG home rather than the runner's default keyring. That isolation is a security improvement, and it is also the change most likely to break an existing signing step.
The passphrase now travels through gpg.passphraseEnvName rather than a deprecated gpg.passphrase server entry in settings.xml, and the README states this requires maven-gpg-plugin 3.2.0 or newer. Older plugin versions will not pick the passphrase up.
Three inputs were renamed so they are not mistaken for secret values: server-username to server-username-env-var, server-password to server-password-env-var, and gpg-passphrase to gpg-passphrase-env-var. The README says the deprecated aliases still work but emit warnings. The same applies to jdkFile, renamed to jdk-file in v5.
One more v6 change deserves attention: problem-matcher: false disables the Java compiler and uncaught-exception annotations. Teams that find the annotations noisy can turn them off, but the annotations are also how a compiler error surfaces on the diff.
Where setup-java is the wrong choice, and what to use instead
The clearest limitation is scope. This action only runs inside GitHub Actions. If your CI is Jenkins, GitLab CI or CircleCI, none of it applies, and the closest equivalent is a different tool per platform: Jenkins tool installations, a Docker image with the JDK baked in, or a version manager such as SDKMAN or asdf invoked directly in the job script.
The difference in approach matters. A Docker image pins the entire environment, including the JDK, and produces the same result locally with docker run. setup-java pins one component and leaves the rest of the runner image in place. The Docker route is more reproducible and heavier to maintain; the action route is lighter and inherits whatever the runner image changes underneath it.
A second limitation is the version resolution path. Because java-version: latest and cached metadata both resolve against vendor state, a workflow that always wants the newest release will drift as vendors publish. Teams that need byte-identical rebuilds should pin an exact version and set force-download: true rather than trusting the cache.
Finally, v6 removed the legacy AdoptOpenJDK distributions. Workflows using adopt, adopt-hotspot or adopt-openj9 must move to temurin or semeru. The README is explicit about this, and it is the kind of change that fails loudly rather than silently.
Licence, maintenance and the cost of staying on an old major
The action is MIT licensed, and the repository carries a .licensed.yml plus a .licenses/ directory, which suggests dependency licence checking runs in CI. MIT is permissive, so consuming the action in a commercial workflow raises no obvious licensing question, but that is a general observation about the licence text and not legal advice about your situation.
The README states that versions v1 through v4 are deprecated and asks users to upgrade to v6. The last push to the repository was on 2026-08-24, the same day v6.0.0 was released, with v4.9.1 and v3.14.2 following on 2026-08-04. Maintenance patches are still landing on the older major lines, which softens the upgrade pressure but does not remove it.
Upgrade cost is concentrated in three places: the Node runtime bump (v5 moved from Node 20 to Node 24, and self-hosted runners must be on runner version v2.327.1 or later), the renamed credential inputs, and the GPG passphrase mechanism requiring maven-gpg-plugin 3.2.0 or newer. A workflow that only installs a JDK and runs Maven has almost nothing to change. A workflow that publishes signed artifacts has all three to check.
Editorial conclusion
Adopt actions/setup-java if your build runs on GitHub-hosted or self-hosted runners and you want the JDK pinned by a workflow file rather than by a machine image. Do not adopt it if you build Java outside GitHub Actions, or if you depend on AdoptOpenJDK identifiers, which v6 removed. Before upgrading, check three things: that every workflow references v6 rather than v1 through v4, that self-hosted runners are on runner version v2.327.1 or later, and that any Maven GPG signing step uses maven-gpg-plugin 3.2.0 or newer, because v6 passes the passphrase through gpg.passphraseEnvName instead of a settings.xml server entry.
Frequently asked questions
How do I set up Java with actions/setup-java in a GitHub Actions workflow?
Reference the action with a distribution and a version, then run your build. The README's example uses actions/setup-java@v6 with distribution: temurin and java-version: '25', followed by a run step that prints java --version.
Do I need to install a JDK separately if I use actions/setup-java?
No. The action downloads and installs Java from a supported distribution, adds it to PATH and configures JAVA_HOME, so the runner does not need a preinstalled JDK at that version.
Is actions/setup-java safe to use with downloaded JDK archives?
The README states that JDK downloads automatically verify authoritative checksums when a distribution publishes them, and that package signature verification defaults to enabled for Temurin and Microsoft builds. Verification depends on the distribution publishing the relevant data.
How do I install and set up a Java environment for a build?
In GitHub Actions the action handles it: it installs the requested distribution, adds it to PATH and configures JAVA_HOME, and can read the version from .java-version, .tool-versions or .sdkmanrc instead of a hardcoded string.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/actions-setup-java)