# Optimal Dockerfile for Java with Gradle (https://depot.dev/docs/container-builds/optimal-dockerfiles/java-gradle-dockerfile)

Below is an example `Dockerfile` that we recommend at Depot for building images for Java applications with Gradle.

```dockerfile
# syntax=docker/dockerfile:1

FROM eclipse-temurin:21-jdk AS build

ENV GRADLE_HOME=/opt/gradle \
    GRADLE_USER_HOME=/cache/.gradle \
    GRADLE_OPTS="-Dorg.gradle.daemon=false \
    -Dorg.gradle.parallel=true \
    -Dorg.gradle.caching=true \
    -Xmx2g"

ARG GRADLE_VERSION=8.10
RUN apt-get update && apt-get install -y --no-install-recommends unzip \
    && wget -q https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip \
    && unzip gradle-${GRADLE_VERSION}-bin.zip -d /opt \
    && ln -s /opt/gradle-${GRADLE_VERSION} /opt/gradle \
    && rm gradle-${GRADLE_VERSION}-bin.zip \
    && apt-get remove -y unzip \
    && rm -rf /var/lib/apt/lists/*

ENV PATH="${GRADLE_HOME}/bin:${PATH}"

WORKDIR /app

COPY build.gradle ./

RUN --mount=type=cache,target=/cache/.gradle \
    gradle dependencies --no-daemon --stacktrace

COPY src/ src/

RUN --mount=type=cache,target=/cache/.gradle \
    gradle build -x test --no-daemon --stacktrace --build-cache

FROM eclipse-temurin:21-jre AS runtime

RUN groupadd -g 1001 appgroup && \
    useradd -u 1001 -g appgroup -m -d /app -s /bin/false appuser

WORKDIR /app

COPY --from=build --chown=appuser:appgroup /app/build/libs/*.jar app.jar

ENV JAVA_OPTS="-server \
    -XX:+UseContainerSupport \
    -XX:MaxRAMPercentage=75.0 \
    -XX:+UseG1GC \
    -Djava.security.egd=file:/dev/./urandom"

USER appuser

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
```

## Explanation of the Dockerfile

At a high level, here are the things we're optimizing in our Docker build for a Java application with Gradle:

* Multi-stage builds for smaller final images
* Gradle cache mounts for dependency and build caching
* Gradle build optimizations for container environments
* Security optimizations with non-root users

### Stage 1: `FROM eclipse-temurin:21-jdk AS build`

```dockerfile
FROM eclipse-temurin:21-jdk AS build

ENV GRADLE_HOME=/opt/gradle \
    GRADLE_USER_HOME=/cache/.gradle \
    GRADLE_OPTS="-Dorg.gradle.daemon=false \
    -Dorg.gradle.parallel=true \
    -Dorg.gradle.caching=true \
    -Xmx2g"
```

We use Eclipse Temurin 21 JDK and configure Gradle with optimized settings:

* `GRADLE_USER_HOME=/cache/.gradle` points to our cache mount location
* `gradle.daemon=false` disables the daemon (not beneficial in containers)
* `gradle.parallel=true` enables parallel execution for faster builds
* `gradle.caching=true` enables Gradle's build cache
* `-Xmx2g` sets maximum heap size for Gradle

#### Installing Gradle

```dockerfile
ARG GRADLE_VERSION=8.10
RUN apt-get update && apt-get install -y --no-install-recommends unzip \
    && wget -q https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip \
    && unzip gradle-${GRADLE_VERSION}-bin.zip -d /opt \
    && ln -s /opt/gradle-${GRADLE_VERSION} /opt/gradle \
    && rm gradle-${GRADLE_VERSION}-bin.zip \
    && apt-get remove -y unzip \
    && rm -rf /var/lib/apt/lists/*

ENV PATH="${GRADLE_HOME}/bin:${PATH}"
```

We install a specific Gradle version for reproducible builds and clean up build tools afterward to keep the layer small.

#### Dependency resolution and caching

```dockerfile
WORKDIR /app

COPY build.gradle ./

RUN --mount=type=cache,target=/cache/.gradle \
    gradle dependencies --no-daemon --stacktrace
```

We copy only the `build.gradle` first to leverage Docker layer caching. The `dependencies` task downloads all dependencies, with a cache mount to persist between builds.

#### Building the application

```dockerfile
COPY src/ src/

RUN --mount=type=cache,target=/cache/.gradle \
    gradle build -x test --no-daemon --stacktrace --build-cache
```

After copying the source code, we build the application with the same cache mount. Key options:

* `-x test` excludes tests from the build (run in CI/CD pipeline)
* `--no-daemon` ensures no daemon process is left running
* `--build-cache` enables Gradle's build cache for faster incremental builds

### Stage 2: `FROM eclipse-temurin:21-jre AS runtime`

```dockerfile
FROM eclipse-temurin:21-jre AS runtime

RUN groupadd -g 1001 appgroup && \
    useradd -u 1001 -g appgroup -m -d /app -s /bin/false appuser

WORKDIR /app

COPY --from=build --chown=appuser:appgroup /app/build/libs/*.jar app.jar

ENV JAVA_OPTS="-server \
    -XX:+UseContainerSupport \
    -XX:MaxRAMPercentage=75.0 \
    -XX:+UseG1GC \
    -Djava.security.egd=file:/dev/./urandom"

USER appuser

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
```

The runtime stage uses Eclipse Temurin 21 JRE for a reliable runtime environment. We create a non-root user for security and copy the built JAR file. The JVM is configured with production settings:

* `-server` enables server mode for better long-running performance
* `UseContainerSupport` and `MaxRAMPercentage` for container-aware memory management
* `UseG1GC` enables the G1 garbage collector for better performance
* `java.security.egd` uses `/dev/urandom` for faster startup

## Understanding BuildKit Cache Mounts

Cache mounts in this Dockerfile speed up builds by persisting the package manager cache. This means that even when a layer needs to be rebuilt, your package manager only fetches what's new or updated. This Dockerfile uses the following cache mount syntax:

```dockerfile
RUN --mount=type=cache,target=/cache/.gradle \
    gradle dependencies --no-daemon --stacktrace
```

### Cache Mount Parameters Explained

* **`type=cache`**: Specifies this is a cache mount that persists across builds.
* **`target=/cache/.gradle`**: The mount point for Gradle's cache directory (configured via `GRADLE_USER_HOME`).

For more information regarding Gradle cache mounts, please visit the official [Gradle documentation](https://docs.gradle.org/current/userguide/build_cache.html).

## For AI Agents

The full site index is at [llms.txt](https://depot.dev/llms.txt). Append `.md` to any documentation, blog, changelog, or customer URL to fetch its markdown source directly.