diff --git a/native-image/micronaut-webserver/Dockerfile.distroless-base.mostly b/native-image/micronaut-webserver/Dockerfile.distroless-base.mostly
index 811491c..7dd5a36 100644
--- a/native-image/micronaut-webserver/Dockerfile.distroless-base.mostly
+++ b/native-image/micronaut-webserver/Dockerfile.distroless-base.mostly
@@ -8,7 +8,7 @@ RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image -Pmostl
# RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image -DbuildArgs="--static-nolibc,-Os,-o target/webserver.mostly-static"
# Distroless Base - provides glibc
-FROM gcr.io/distroless/base-debian12
+FROM gcr.io/distroless/base-debian13
COPY --from=nativebuild /webserver/target/webserver.mostly-static /
EXPOSE 8000
ENTRYPOINT ["/webserver.mostly-static", "-b", "0.0.0.0", "-d", "/web"]
\ No newline at end of file
diff --git a/native-image/micronaut-webserver/Dockerfile.distroless-java-base-jar b/native-image/micronaut-webserver/Dockerfile.distroless-java-base-jar
index 79998e6..e2ba709 100644
--- a/native-image/micronaut-webserver/Dockerfile.distroless-java-base-jar
+++ b/native-image/micronaut-webserver/Dockerfile.distroless-java-base-jar
@@ -4,7 +4,7 @@ WORKDIR /webserver
RUN ./mvnw --no-transfer-progress clean package
# Distoless Java 21 (Debian)
-FROM gcr.io/distroless/java21-debian12
+FROM gcr.io/distroless/java25-debian13
COPY --from=build /webserver/target/webserver-0.1.jar webserver-0.1.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "webserver-0.1.jar"]
\ No newline at end of file
diff --git a/native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic b/native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic
index 251d5df..2f85b37 100644
--- a/native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic
+++ b/native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic
@@ -5,7 +5,7 @@ WORKDIR /webserver
RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image
# Distroless Java Base - provides glibc and other libraries needed by the JDK
-FROM gcr.io/distroless/java-base-debian12
+FROM gcr.io/distroless/java-base-debian13
COPY --from=nativebuild /webserver/target/webserver /
EXPOSE 8000
ENTRYPOINT ["/webserver", "-b", "0.0.0.0", "-d", "/web"]
\ No newline at end of file
diff --git a/native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic-optimized b/native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic-optimized
index 9e474c9..b339e0b 100644
--- a/native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic-optimized
+++ b/native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic-optimized
@@ -8,7 +8,7 @@ RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image -Pdynam
# RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image -DbuildArgs="-Os,-o target/webserver.dynamic-optimized"
# Distroless Java Base-provides glibc and other libraries needed by the JDK
-FROM gcr.io/distroless/java-base-debian12
+FROM gcr.io/distroless/java-base-debian13
COPY --from=nativebuild /webserver/target/webserver.dynamic-optimized /
EXPOSE 8000
ENTRYPOINT ["/webserver.dynamic-optimized", "-b", "0.0.0.0", "-d", "/web"]
\ No newline at end of file
diff --git a/native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic-skipflow b/native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic-skipflow
new file mode 100644
index 0000000..4852737
--- /dev/null
+++ b/native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic-skipflow
@@ -0,0 +1,14 @@
+FROM container-registry.oracle.com/graalvm/native-image:25 AS nativebuild
+COPY . /webserver
+WORKDIR /webserver
+# Build a dynamically linked native image with optimization for size
+RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image -Pdynamic-skipflow-optimized
+
+# Alternative way to pass Native Image build options with `-DbuildArgs` without using Maven profiles:
+# RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image -DbuildArgs="-Os,-H:+UnlockExperimentalVMOptions,-H:+TrackPrimitiveValues,-H:+UsePredicates,-o target/webserver.dynamic-skipflow"
+
+# Distroless Java Base-provides glibc and other libraries needed by the JDK
+FROM gcr.io/distroless/java-base-debian13
+COPY --from=nativebuild /webserver/target/webserver.dynamic-skipflow /
+EXPOSE 8000
+ENTRYPOINT ["/webserver.dynamic-skipflow", "-b", "0.0.0.0", "-d", "/web"]
\ No newline at end of file
diff --git a/native-image/micronaut-webserver/Dockerfile.distroless-java-base.jlink b/native-image/micronaut-webserver/Dockerfile.distroless-java-base.jlink
index 6f7d3c6..e1a2be2 100644
--- a/native-image/micronaut-webserver/Dockerfile.distroless-java-base.jlink
+++ b/native-image/micronaut-webserver/Dockerfile.distroless-java-base.jlink
@@ -18,7 +18,7 @@ RUN CP=$(cat cp.txt) && \
--output jlink-jre
# Distroless Java Base-provides glibc and other libraries needed by the JDK
-FROM gcr.io/distroless/java-base-debian12
+FROM gcr.io/distroless/java-base-debian13
COPY --from=build /webserver/target/webserver-0.1.jar webserver-0.1.jar
COPY --from=build /webserver/jlink-jre jlink-jre
EXPOSE 8080
diff --git a/native-image/micronaut-webserver/Dockerfile.eclipse-temurin-jar b/native-image/micronaut-webserver/Dockerfile.eclispe-temurin-jar
similarity index 100%
rename from native-image/micronaut-webserver/Dockerfile.eclipse-temurin-jar
rename to native-image/micronaut-webserver/Dockerfile.eclispe-temurin-jar
diff --git a/native-image/micronaut-webserver/README.md b/native-image/micronaut-webserver/README.md
index 359e620..0c32896 100644
--- a/native-image/micronaut-webserver/README.md
+++ b/native-image/micronaut-webserver/README.md
@@ -28,7 +28,7 @@ In this workshop you will:
* x86 Linux
* `musl` toolchain
* Container runtime such as [Docker](https://www.docker.com/gettingstarted/), or [Rancher Desktop](https://docs.rancherdesktop.io/getting-started/installation/) installed and running.
-* [GraalVM 25](https://www.graalvm.org/downloads/). We recommend using [SDKMAN!](https://sdkman.io/). (For other download options, see [GraalVM Downloads](https://www.graalvm.org/downloads/).)
+* [Oracle GraalVM 25](https://www.graalvm.org/downloads/). We recommend using [SDKMAN!](https://sdkman.io/). (For other download options, see [GraalVM Downloads](https://www.graalvm.org/downloads/).)
```bash
sdk install java 25-graal
```
@@ -53,7 +53,7 @@ In this workshop you will:
./build-jar-eclipse-temurin.sh
```
Once the script finishes, a container image _eclipse-temurin-jar_ should be available.
- Check its size. It should be **472MB**.
+ Check its size. It should be **472MB**.
```bash
docker images
```
@@ -67,7 +67,7 @@ It requires a container image with a JDK and runtime libraries.
### Explanation
-The Dockerfile provided for this step pulls [container-registry.oracle.com/graalvm/jdk:24](https://docs.oracle.com/en/graalvm/jdk/24/docs/getting-started/container-images/) for the builder, and then `gcr.io/distroless/java21-debian12` for the runtime.
+The Dockerfile provided for this step pulls [container-registry.oracle.com/graalvm/jdk:25](https://docs.oracle.com/en/graalvm/jdk/25/docs/getting-started/container-images/) for the builder, and then `gcr.io/distroless/java25-debian13` for the runtime.
The entrypoint for this image is equivalent to `java -jar`, so only a path to a JAR file is specified in `CMD`.
### Action
@@ -112,7 +112,7 @@ See how much reduction in size you can gain.
Introduced in Java 11, it provides a way to make applications more space efficient and cloud-friendly.
The script _build-jlink.sh_ that runs `docker build` using the _Dockerfile.distroless-java-base.jlink_.
-The Dockerfile contains two stages: first it generates a `jlink` custom runtime on a full JDK (`container-registry.oracle.com/graalvm/jdk:24`); then copies the runtime image folder along with static assets into a distroless Java base image, and sets the entrypoint.
+The Dockerfile contains two stages: first it generates a `jlink` custom runtime on a full JDK (`container-registry.oracle.com/graalvm/jdk:25`); then copies the runtime image folder along with static assets into a distroless Java base image, and sets the entrypoint.
Distroless Java base image provides `glibc` and other libraries needed by the JDK, **but not a full-blown JDK**.
The application does not have to be modular, but you need to figure out which modules the application depends on to be able to `jlink` it.
@@ -238,7 +238,7 @@ In this step, you will build a fully dynamically linked native image **with the
GraalVM Native Image provides the option `-Os` which optimizes the resulting native image for file size.
`-Os` enables `-O2` optimizations except those that can increase code or executable size significantly.
-Learn more about different optimization levels in the [Native Image documentation](https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/#optimization-levels).
+Learn more about different optimization levels in the [Native Image documentation](https://www.graalvm.org/jdk25/reference-manual/native-image/optimizations-and-performance/#optimization-levels).
To configure the Native Image build and have more manual control over the process, GraalVM provides the [Native Build Tools](https://graalvm.github.io/native-build-tools/latest/index.html): Maven and Gradle plugins for building native images.
@@ -315,7 +315,85 @@ No Java Runtime Environment (JRE) is required.
The size of the container came down from **132MB** to **102MB**.
The executable size decreased by **24MB** (from 86MB to 62MB) just by applying the file size optimization - with no change in behavior or startup time!
-## **STEP 5**: Build a Size-Optimized Mostly Static Native Image and Run Inside a Container
+
+## **STEP 5**: (Optional) Build a Size-Optimized Native Image with SkipFlow and Run Inside a Container
+
+In this step, you will build another fully dynamically linked native image but with the **SkipFlow** and **file size** optimizations on. Then you run it inside a container.
+
+### Explanation
+
+As of Oracle GraalVM 25, more performance improvements are enabled by default.
+One of which is [SkipFlow](https://www.graalvm.org/release-notes/JDK_25/#native-image)-an extension to the Native Image static analysis that tracks primitive values and evaluates branching conditions dynamically during the process.
+
+Note: The feature is enabled by default. With the previous releases, it could be controlled using these host options: `-H:+TrackPrimitiveValues` and `-H:+UsePredicates`.
+
+For this, we have added a separate Maven profile with a different name for the generated native executable:
+```xml
+
+ dynamic-skipflow-optimized
+
+
+
+ org.graalvm.buildtools
+ native-maven-plugin
+
+ webserver.dynamic-skipflow
+
+ -Os
+ -H:+UnlockExperimentalVMOptions
+ -H:+TrackPrimitiveValues
+ -H:+UsePredicates
+
+
+
+
+
+
+```
+
+> An alternative way to pass build options to Native Image, without creating Maven profiles, is by using `-DbuildArgs`:
+```bash
+./mvnw package -Dpackaging=native-image -DbuildArgs="-Os,-H:+UnlockExperimentalVMOptions,-H:+TrackPrimitiveValues,-H:+UsePredicates,-o target/webserver.dynamic-skipflow"
+```
+
+The Dockerfile for this step, _Dockerfile.distroless-java-base.dynamic-skipflow_, is pretty much the same as before: running a native image build inside the builder container, and then copying it over to a distroless base container with just enough to run the application.
+No Java Runtime Environment (JRE) is required.
+
+### Action
+
+1. Run the script to build a size-optimized native executable and package it into a container:
+ ```bash
+ ./build-dynamic-image-skipflow.sh
+ ```
+
+2. Once the build completes, a container image _distroless-java-base.dynamic-optimized_ should be available. Run it, mapping the ports:
+ ```bash
+ docker run --rm -p8080:8080 webserver:distroless-java-base.dynamic-skipflow
+ ```
+ The application is running from the native image inside a container.
+ The startup time has not changed.
+
+3. Open a browser and navigate to [localhost:8080/](http://localhost:8080/). You see the GraalVM documentation pages served.
+
+4. Return to the terminal and stop the running container by clicking CTRL+C.
+
+5. Check the size of this container image:
+ ```bash
+ docker images
+ ```
+ The expected output is:
+ ```bash
+ REPOSITORY TAG IMAGE ID CREATED SIZE
+ webserver distroless-java-base.dynamic-skipflow 6caada87f616 8 minutes ago 101MB
+ webserver distroless-java-base.dynamic-optimized 5e16a58b1649 10 minutes ago 102MB
+ webserver distroless-java-base.dynamic d7c449b9373d 12 minutes ago 132MB
+ webserver distroless-java-base.jlink dde1eb772aa5 15 minutes ago 167MB
+ webserver distroless-java-base.jar e285476a8266 32 minutes ago 216MB
+ webserver eclispe-temurin-jar f6eef8d2aa40 33 minutes ago 472MB
+ ```
+ The gain is tiny: the container size reduced only by 1MB, but depending on the application, **SkipFlow can provide up to a 4% reduction in binary size without any additional impact on build time**.
+
+## **STEP 6**: Build a Size-Optimized Mostly Static Native Image and Run Inside a Container
In this step, you will build a **mostly static** native image, with the file size optimization on, and then package it into a container image that provides `glibc`, and run.
@@ -555,12 +633,13 @@ Note that the website static pages add 44MB to the container images size. Static
| Container | Size of a build artefact
(JAR, Jlink runtime, native executable) | Base image | Container |
|----------------------------------------|-----------------------------------------------------------------------|------------|-----------|
-| eclipse-temurin-jar | webserver-0.1.jar **24MB** | eclipse-temurin:25 201MB | 472MB |
-| distroless-java-base.jar | webserver-0.1.jar **24MB** | java21-debian12 192MB | 216MB |
-| distroless-java-base.jlink | jlink-jre custom runtime **68MB** | java-base-debian12 128MB | 167MB |
-| distroless-java-base.dynamic | webserver.dynamic **86MB** | java-base-debian12 128MB | 132MB |
-| distroless-java-base.dynamic-optimized | webserver.dynamic-optimized **62MB** | java-base-debian12 128MB | 102MB |
-| distroless-base.mostly-static | webserver.mostly-static **62MB** | base-debian12 48.3MB | 89.7MB |
+| eclispe-temurin-jar | webserver-0.1.jar **24MB** | eclipse-temurin:25 201MB | 472MB |
+| distroless-java-base.jar | webserver-0.1.jar **24MB** | java25-debian13 192MB | 216MB |
+| distroless-java-base.jlink | jlink-jre custom runtime **68MB** | java-base-debian13 128MB | 167MB |
+| distroless-java-base.dynamic | webserver.dynamic **86MB** | java-base-debian13 128MB | 132MB |
+| distroless-java-base.dynamic-optimized | webserver.dynamic-optimized **62MB** | java-base-debian13 128MB | 102MB |
+| distroless-java-base.dynamic-skipflow | webserver.dynamic-skipflow **61MB** | java-base-debian13 128MB | 101MB |
+| distroless-base.mostly-static | webserver.mostly-static **62MB** | base-debian13 48.3MB | 89.7MB |
| scratch.static | webserver.static **62MB** | scratch 2MB | 69.2MB |
| scratch.static-upx | webserver.scratch.static-upx **20MB** | scratch 2MB | 22.3MB |
diff --git a/native-image/micronaut-webserver/build-all.sh b/native-image/micronaut-webserver/build-all.sh
index 446bd19..c5efa30 100755
--- a/native-image/micronaut-webserver/build-all.sh
+++ b/native-image/micronaut-webserver/build-all.sh
@@ -1,7 +1,7 @@
#!/bin/sh
-./build-jar-eclipse-temurin.sh
./build-jar-java-base.sh
+./build-jar-eclipse-temurin.sh
./build-jlink.sh
./build-dynamic-image.sh
./build-dynamic-image-optimized.sh
diff --git a/native-image/micronaut-webserver/build-jar-eclipse-temurin.sh b/native-image/micronaut-webserver/build-jar-eclipse-temurin.sh
index 82dee3c..c85d5ae 100755
--- a/native-image/micronaut-webserver/build-jar-eclipse-temurin.sh
+++ b/native-image/micronaut-webserver/build-jar-eclipse-temurin.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-# Eclipse Temurin Java 25
-docker build --no-cache . -f Dockerfile.eclipse-temurin-jar -t webserver:eclipse-temurin-jar
\ No newline at end of file
+# Eclipse-temurin:25
+docker build --no-cache . -f Dockerfile.eclispe-temurin-jar -t webserver:eclipse-temurin-jar
diff --git a/native-image/micronaut-webserver/build-jar-java-base.sh b/native-image/micronaut-webserver/build-jar-java-base.sh
index d190b7c..0b696d8 100755
--- a/native-image/micronaut-webserver/build-jar-java-base.sh
+++ b/native-image/micronaut-webserver/build-jar-java-base.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-# Distroless Java 21 (Debian)-provides glibc and other libraries needed by the JDK
+# Distroless Java 25 (Debian)-provides glibc and other libraries needed by the JDK
docker build --no-cache . -f Dockerfile.distroless-java-base-jar -t webserver:distroless-java-base.jar
\ No newline at end of file
diff --git a/native-image/micronaut-webserver/pom.xml b/native-image/micronaut-webserver/pom.xml
index 3a03d0e..05a72da 100644
--- a/native-image/micronaut-webserver/pom.xml
+++ b/native-image/micronaut-webserver/pom.xml
@@ -19,9 +19,9 @@
jar
- 21
- 21
- 4.9.2
+ 25
+ 25
+ 4.8.2
netty
false
com.example.webserver.Application
diff --git a/native-image/micronaut-webserver/src/main/resources/static.zip b/native-image/micronaut-webserver/src/main/resources/static.zip
index 6f620dd..de0fca1 100644
Binary files a/native-image/micronaut-webserver/src/main/resources/static.zip and b/native-image/micronaut-webserver/src/main/resources/static.zip differ
diff --git a/native-image/spring-boot-webserver/Dockerfile.distroless-base.mostly b/native-image/spring-boot-webserver/Dockerfile.distroless-base.mostly
index c59ffb3..a8624f1 100644
--- a/native-image/spring-boot-webserver/Dockerfile.distroless-base.mostly
+++ b/native-image/spring-boot-webserver/Dockerfile.distroless-base.mostly
@@ -5,7 +5,7 @@ WORKDIR /webserver
RUN ./mvnw -Dmaven.test.skip=true -Pnative,mostly-static native:compile
# Distroless Base - provides glibc
-FROM gcr.io/distroless/base-debian12
+FROM gcr.io/distroless/base-debian13
COPY --from=nativebuild /webserver/target/webserver.mostly-static /
EXPOSE 8000
ENTRYPOINT ["/webserver.mostly-static", "-b", "0.0.0.0", "-d", "/web"]
\ No newline at end of file
diff --git a/native-image/spring-boot-webserver/Dockerfile.distroless-java-base-jar b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base-jar
index 9e07b01..78e4011 100644
--- a/native-image/spring-boot-webserver/Dockerfile.distroless-java-base-jar
+++ b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base-jar
@@ -3,9 +3,8 @@ COPY . /webserver
WORKDIR /webserver
RUN ./mvnw clean package
-##### TODO: Upgrade to Distroless Java 25 (Debian)
-# Distoless Java 21 (Debian)
-FROM gcr.io/distroless/java21-debian12
+# Distoless Java 25 (Debian)
+FROM gcr.io/distroless/java25-debian13
COPY --from=build /webserver/target/webserver-0.0.1-SNAPSHOT.jar webserver-0.0.1-SNAPSHOT.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "webserver-0.0.1-SNAPSHOT.jar"]
\ No newline at end of file
diff --git a/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic
index 9eb0db1..52e1790 100644
--- a/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic
+++ b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic
@@ -5,7 +5,7 @@ WORKDIR /webserver
RUN ./mvnw -Dmaven.test.skip=true -Pnative native:compile
# Distroless Java Base - provides glibc and other libraries needed by the JDK
-FROM gcr.io/distroless/java-base-debian12
+FROM gcr.io/distroless/java-base-debian13
COPY --from=nativebuild /webserver/target/webserver.dynamic /
EXPOSE 8000
ENTRYPOINT ["/webserver.dynamic", "-b", "0.0.0.0", "-d", "/web"]
\ No newline at end of file
diff --git a/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic-optimized b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic-optimized
index 08bb21b..013e40d 100644
--- a/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic-optimized
+++ b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic-optimized
@@ -5,7 +5,7 @@ WORKDIR /webserver
RUN ./mvnw -Dmaven.test.skip=true -Pnative,dynamic-size-optimized native:compile
# Distroless Java Base-provides glibc and other libraries needed by the JDK
-FROM gcr.io/distroless/java-base-debian12
+FROM gcr.io/distroless/java-base-debian13
COPY --from=nativebuild /webserver/target/webserver.dynamic-optimized /
EXPOSE 8000
ENTRYPOINT ["/webserver.dynamic-optimized", "-b", "0.0.0.0", "-d", "/web"]
\ No newline at end of file
diff --git a/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic-skipflow b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic-skipflow
new file mode 100644
index 0000000..1eac0cb
--- /dev/null
+++ b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic-skipflow
@@ -0,0 +1,11 @@
+FROM container-registry.oracle.com/graalvm/native-image:25 AS nativebuild
+COPY . /webserver
+WORKDIR /webserver
+# Build a dynamically linked native image with optimization for size
+RUN ./mvnw -Dmaven.test.skip=true -Pnative,dynamic-skipflow-optimized native:compile
+
+# Distroless Java Base-provides glibc and other libraries needed by the JDK
+FROM gcr.io/distroless/java-base-debian13
+COPY --from=nativebuild /webserver/target/webserver.dynamic-skipflow /
+EXPOSE 8000
+ENTRYPOINT ["/webserver.dynamic-skipflow", "-b", "0.0.0.0", "-d", "/web"]
\ No newline at end of file
diff --git a/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.jlink b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.jlink
index a091ee2..4727c1e 100644
--- a/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.jlink
+++ b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.jlink
@@ -16,7 +16,7 @@ RUN jlink \
--output jlink-jre
# Distroless Java Base-provides glibc and other libraries needed by the JDK
-FROM gcr.io/distroless/java-base-debian12
+FROM gcr.io/distroless/java-base-debian13
COPY --from=build /webserver/target/webserver-0.0.1-SNAPSHOT.jar webserver-0.0.1-SNAPSHOT.jar
COPY --from=build /webserver/jlink-jre jlink-jre
EXPOSE 8080
diff --git a/native-image/spring-boot-webserver/Dockerfile.eclipse-temurin-jar b/native-image/spring-boot-webserver/Dockerfile.eclipse-temurin-jar
index 7754357..4cfdfee 100644
--- a/native-image/spring-boot-webserver/Dockerfile.eclipse-temurin-jar
+++ b/native-image/spring-boot-webserver/Dockerfile.eclipse-temurin-jar
@@ -3,7 +3,7 @@ COPY . /webserver
WORKDIR /webserver
RUN ./mvnw clean package
-# Eclipse Temurin Java 25
+# Distoless Java 25 (Debian)
FROM eclipse-temurin:25
COPY --from=build /webserver/target/webserver-0.0.1-SNAPSHOT.jar webserver-0.0.1-SNAPSHOT.jar
EXPOSE 8080
diff --git a/native-image/spring-boot-webserver/README.md b/native-image/spring-boot-webserver/README.md
index 72dddb2..d9f966a 100644
--- a/native-image/spring-boot-webserver/README.md
+++ b/native-image/spring-boot-webserver/README.md
@@ -28,7 +28,7 @@ In this workshop you will:
* x86 Linux
* `musl` toolchain
* Container runtime such as [Docker](https://www.docker.com/gettingstarted/), or [Rancher Desktop](https://docs.rancherdesktop.io/getting-started/installation/) installed and running.
-* [GraalVM 25](https://www.graalvm.org/downloads/). We recommend using [SDKMAN!](https://sdkman.io/). (For other download options, see [GraalVM Downloads](https://www.graalvm.org/downloads/).)
+* [Oracle GraalVM 25](https://www.graalvm.org/downloads/). We recommend using [SDKMAN!](https://sdkman.io/). (For other download options, see [GraalVM Downloads](https://www.graalvm.org/downloads/).)
```bash
sdk install java 25-graal
```
@@ -67,7 +67,7 @@ It requires a container image with a full JDK and runtime libraries.
### Explanation
-The Dockerfile provided for this step pulls [container-registry.oracle.com/graalvm/jdk:25](https://docs.oracle.com/en/graalvm/jdk/25/docs/getting-started/container-images/) for the builder, and then `gcr.io/distroless/java21-debian12` for the runtime.
+The Dockerfile provided for this step pulls [container-registry.oracle.com/graalvm/jdk:25](https://docs.oracle.com/en/graalvm/jdk/25/docs/getting-started/container-images/) for the builder, and then `gcr.io/distroless/java25-debian13` for the runtime.
The entrypoint for this image is equivalent to `java -jar`, so only a path to a JAR file is specified in `CMD`.
### Action
@@ -285,7 +285,7 @@ In this step, you will build a fully dynamically linked native image **with the
GraalVM Native Image provides the option `-Os` which optimizes the resulting native image for file size.
`-Os` enables `-O2` optimizations except those that can increase code or executable size significantly.
-Learn more in [the Native Image documentation](https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/#optimization-levels).
+Learn more in [the Native Image documentation](https://www.graalvm.org/jdk25/reference-manual/native-image/optimizations-and-performance/#optimization-levels).
For that, a separate Maven profile is provided to differentiate this run from the default build, and giving a different name for the output file:
```xml
@@ -348,7 +348,78 @@ The Dockerfile for this step, _Dockerfile.distroless-java-base.dynamic-optimized
The size of the native executable decreased from **103MB** to **69MB** by applying the file size optimization!
-## **STEP 6**: Build a Size-Optimized Mostly Static Native Image and Run Inside a Container
+## **STEP 6**: (Optional) Build a Size-Optimized Native Image with SkipFlow and Run Inside a Container
+
+In this step, you will build another fully dynamically linked native image but with the **SkipFlow** and **file size** optimizations on. Then you run it inside a container.
+
+### Explanation
+
+As of Oracle GraalVM 25, more performance improvements are enabled by default. One of which is [SkipFlow](https://www.graalvm.org/release-notes/JDK_25/#native-image)-an extension to the Native Image static analysis that tracks primitive values and evaluates branching conditions dynamically during the process.
+
+Note: The feature is enabled by default. With the previous releases, it could be controlled using these host options: `-H:+TrackPrimitiveValues` and `-H:+UsePredicates`.
+
+For that, a separate Maven profile is provided, giving a different name for the output file:
+```xml
+
+ dynamic-skipflow-optimized
+
+
+
+ org.graalvm.buildtools
+ native-maven-plugin
+
+ webserver.dynamic-skipflow
+
+ -Os
+ -H:+UnlockExperimentalVMOptions
+ -H:+TrackPrimitiveValues
+ -H:+UsePredicates
+
+
+
+
+
+
+```
+
+The Dockerfile for this step, _Dockerfile.distroless-java-base.dynamic-skipflow_, is pretty much the same as before: running a native image build inside the builder container, and then copying it over to a distroless base container with just enough to run the application. No Java Runtime Environment (JRE) is required.
+
+### Action
+
+1. Run the script to build a size-optimized native executable and package it into a container:
+ ```bash
+ ./build-dynamic-image-skipflow.sh
+ ```
+
+2. Once the build completes, a container image _distroless-java-base.dynamic-optimized_ should be available. Run it, mapping the ports:
+ ```bash
+ docker run --rm -p8080:8080 webserver:distroless-java-base.dynamic-skipflow
+ ```
+
+ The application is running from the native image inside a container.
+ The startup time has not changed.
+
+3. Open a browser and navigate to [localhost:8080/](http://localhost:8080/). You see the GraalVM documentation pages served.
+
+4. Return to the terminal and stop the running container by clicking CTRL+C.
+
+5. Check the size of this container image:
+ ```bash
+ docker images
+ ```
+ The expected output is:
+ ```bash
+ REPOSITORY TAG IMAGE ID CREATED SIZE
+ webserver distroless-java-base.dynamic-skipflow 7c748db34ef4 2 hours ago 103MB
+ webserver distroless-java-base.dynamic-optimized 5e16a58b1649 2 hours ago 105MB
+ webserver distroless-java-base.dynamic d7c449b9373d 2 hours ago 141MB
+ webserver distroless-java-base.jlink 191efb04958d 2 hours ago 178MB
+ webserver distroless-java-base.jar 846971900174 2 hours ago 225MB
+ webserver.buildpacks latest 615deed5b89c 45 years ago 142MB
+ ```
+ The gain is tiny: the container size reduced only by 2MB, from 105MB to **103MB**, but depending on the application, **SkipFlow can provide up to a 4% reduction in binary size without any additional impact on build time**.
+
+## **STEP 7**: Build a Size-Optimized Mostly Static Native Image and Run Inside a Container
In this step, you will build a **mostly static** native image, with the file size optimization on, and then package it into a container image that provides `glibc`, and run.
@@ -414,9 +485,9 @@ A separate Maven profile exists for this step:
```
The size of the new _distroless-base.mostly-static_ container is **92.7MB**.
- The reduction in size is related to the fact that a smaller base image was pulled: **gcr.io/distroless/base-debian12**.
+ The reduction in size is related to the fact that a smaller base image was pulled: **gcr.io/distroless/base-debian13**.
[Distroless images](https://github.com/GoogleContainerTools/distroless) are very small, and the one used is only **48.3 MB**.
- That's about 50% of the size of **java-base-debian12**(124 MB) used before, and 3 times less than **java21-debian12** (192 MB) containing a full JDK.
+ That's about 50% of the size of **java-base-debian13**(124 MB) used before, and 3 times less than **java25-debian13** (192 MB) containing a full JDK.
The size of the mostly static native image has not changed, and is **69MB**.
@@ -577,13 +648,14 @@ Sorted by size, it is clear that the fully static native image, compressed with
| Container | Size of a build artefact
(JAR, Jlink runtime, native executable) | Base image | Container |
|----------------------------------------|-----------------------------------------------------------------------|------------|-----------|
-| eclipse-temurin-jar | webserver-0.0.1-SNAPSHOT.jar **32M** | eclipse-temurin:25 201MB | 481MB |
-| distroless-java-base.jar | webserver-0.0.1-SNAPSHOT.jar **32M** | java21-debian12 192MB | 225MB |
-| distroless-java-base.jlink | jlink-jre custom runtime **68MB** | java-base-debian12 128MB | 178MB |
-| distroless-java-base.dynamic | webserver.dynamic **103MB** | java-base-debian12 128MB | 141MB |
+| eclispe-temurin-jar | webserver-0.0.1-SNAPSHOT.jar **32M** | eclipse-temurin:25 201MB | 481MB |
+| distroless-java-base.jar | webserver-0.0.1-SNAPSHOT.jar **32M** | java25-debian13 192MB | 225MB |
+| distroless-java-base.jlink | jlink-jre custom runtime **68MB** | java-base-debian13 128MB | 178MB |
+| distroless-java-base.dynamic | webserver.dynamic **103MB** | java-base-debian13 128MB | 141MB |
| webserver.buildpacks:latest | | | 142MB |
-| distroless-java-base.dynamic-optimized | webserver.dynamic-optimized **69MB** | java-base-debian12 128MB | 105MB |
-| distroless-base.mostly-static | webserver.mostly-static **69MB** | base-debian12 48.3MB | 92.7MB |
+| distroless-java-base.dynamic-optimized | webserver.dynamic-optimized **69MB** | java-base-debian13 128MB | 105MB |
+| distroless-java-base.dynamic-skipflow | webserver.dynamic-skipflow **67MB** | java-base-debian13 128MB | 103MB |
+| distroless-base.mostly-static | webserver.mostly-static **69MB** | base-debian13 48.3MB | 92.7MB |
| scratch.static-alpine | webserver.static **69MB** | alpine:3 5MB | 80.5MB |
| scratch.static | webserver.static **69MB** | scratch 2MB | 72.2MB |
| scratch.static-upx | webserver.scratch.static-upx **19MB** | scratch 2MB | 19.1MB |
diff --git a/native-image/spring-boot-webserver/build-jar-eclipse-temurin.sh b/native-image/spring-boot-webserver/build-jar-eclipse-temurin.sh
index 82dee3c..4c44bb0 100755
--- a/native-image/spring-boot-webserver/build-jar-eclipse-temurin.sh
+++ b/native-image/spring-boot-webserver/build-jar-eclipse-temurin.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-# Eclipse Temurin Java 25
-docker build --no-cache . -f Dockerfile.eclipse-temurin-jar -t webserver:eclipse-temurin-jar
\ No newline at end of file
+# Eclipse-temurin:25
+docker build --no-cache . -f Dockerfile.eclipse-temurin-jar -t webserver:eclipse-temurin-jar
diff --git a/native-image/spring-boot-webserver/build-jar-java-base.sh b/native-image/spring-boot-webserver/build-jar-java-base.sh
index d190b7c..0b696d8 100755
--- a/native-image/spring-boot-webserver/build-jar-java-base.sh
+++ b/native-image/spring-boot-webserver/build-jar-java-base.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-# Distroless Java 21 (Debian)-provides glibc and other libraries needed by the JDK
+# Distroless Java 25 (Debian)-provides glibc and other libraries needed by the JDK
docker build --no-cache . -f Dockerfile.distroless-java-base-jar -t webserver:distroless-java-base.jar
\ No newline at end of file
diff --git a/native-image/spring-boot-webserver/pom.xml b/native-image/spring-boot-webserver/pom.xml
index 51561a7..12bca3e 100644
--- a/native-image/spring-boot-webserver/pom.xml
+++ b/native-image/spring-boot-webserver/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.5.6
+ 4.0.1
com.example
@@ -27,7 +27,7 @@
- 21
+ 25
com.example.webserver.WebserverApplication
0.11.1
diff --git a/native-image/spring-boot-webserver/src/main/resources/static.zip b/native-image/spring-boot-webserver/src/main/resources/static.zip
index 6f620dd..de0fca1 100644
Binary files a/native-image/spring-boot-webserver/src/main/resources/static.zip and b/native-image/spring-boot-webserver/src/main/resources/static.zip differ