How to Accelerate Spring Boot Startup on Kubernetes Using CRaC
This guide walks through enabling CRaC in a Spring Boot application, building a CRaC‑compatible Docker image, creating a checkpoint job in Kubernetes, restoring the snapshot at pod start, and comparing the startup speed with GraalVM native compilation.
In this article you will learn how to use Checkpoint/Restore (CRaC) to dramatically reduce Java startup time for a Spring Boot service running on Kubernetes.
What is CRaC?
CRaC (Checkpoint/Restore) is an OpenJDK project introduced by Azul in 2020. It takes a memory snapshot of a running Java process and later restores it, leveraging the Linux CRIU feature. It currently works only on Linux.
Prerequisites
Assume Azul Zulu OpenJDK 17 with built‑in CRaC support is installed on a Linux host.
Creating a checkpoint
Run the application with the checkpoint flag:
$ java -XX:CRaCCheckpointTo=/crac-files -jar target/sample-app.jarThen trigger the checkpoint from another terminal: $ jcmd target/sample-app.jar JDK.checkpoint The command creates a snapshot in /crac-files. A log file dump4.log records the operation.
Restoring the snapshot
Start the application from the snapshot: $ java -XX:CRaCRestoreFrom=/crac-files The startup time drops from seconds to a few milliseconds.
Enabling CRaC for Spring Boot
Spring Boot does not support CRaC out of the box. By replacing the default Tomcat embed with a CRaC‑compatible version, the checkpoint can succeed. Add the following Maven dependencies and exclude the original Tomcat core:
<dependency>
<groupId>io.github.crac.org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>10.1.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</exclusion>
</exclusions>
</dependency>Rebuild the application with the CRaC profile:
$ mvn clean package -PcracBuilding a CRaC‑compatible Docker image
Create a Dockerfile that uses the Azul Zulu OpenJDK 17 image with CRaC support and copies the built uber‑JAR and an entrypoint script:
FROM azul/zulu-openjdk:17-jdk-crac-latest
COPY target/callme-service-1.1.0.jar /app/callme-service-1.1.0.jar
COPY src/scripts/entrypoint.sh /app/entrypoint.sh
RUN chmod 755 /app/entrypoint.shThe entrypoint.sh script starts the JVM, waits, runs jcmd to create the checkpoint, and then sleeps:
#!/bin/bash
java -XX:CRaCCheckpointTo=/crac -jar /app/callme-service-1.1.0.jar &
sleep 10
jcmd /app/callme-service-1.1.0.jar JDK.checkpoint
sleep 10Persisting the checkpoint in Kubernetes
Create a PersistentVolumeClaim to store the snapshot:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: crac-store
namespace: crac
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 10GiDefine a Kubernetes Job that runs the image, executes the entrypoint script, and mounts the PVC. The job needs privileged security context because CRaC requires elevated permissions:
apiVersion: batch/v1
kind: Job
metadata:
name: callme-service-snapshot-job
namespace: crac
spec:
template:
spec:
containers:
- name: callme-service
image: callme-service:1.1.0
env:
- name: VERSION
value: "v1"
command: ["/bin/sh", "-c", "/app/entrypoint.sh"]
volumeMounts:
- mountPath: /crac
name: crac
securityContext:
privileged: true
volumes:
- persistentVolumeClaim:
claimName: crac-store
name: crac
restartPolicy: Never
backoffLimit: 3Apply the job with kubectl apply -f job.yaml. When the pod reaches Completed, the checkpoint is ready.
Deploying the restored service
Deploy three replicas that start the JVM with the restore flag and mount the same PVC:
apiVersion: apps/v1
kind: Deployment
metadata:
name: callme-service
spec:
replicas: 3
selector:
matchLabels:
app: callme-service
template:
metadata:
labels:
app: callme-service
spec:
containers:
- name: callme-service
image: callme-service:1.1.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
env:
- name: VERSION
value: "v1"
command: ["java"]
args: ["-XX:CRaCRestoreFrom=/crac"]
volumeMounts:
- mountPath: /crac
name: crac
readinessProbe:
initialDelaySeconds: 0
periodSeconds: 1
httpGet:
path: /actuator/health/readiness
port: 8080
securityContext:
privileged: true
resources:
limits:
cpu: '1'
volumes:
- name: crac
persistentVolumeClaim:
claimName: crac-storeApply with kubectl apply -f deployment-crac.yaml and observe the pods become ready quickly, allowing a direct comparison of startup latency with and without CRaC.
Comparison with GraalVM
Unlike GraalVM native images, which reduce startup time but increase memory usage and impose stricter limitations, CRaC offers fast startup without a full native compilation step. However, CRaC requires managing snapshot images and mounting persistent volumes for each pod.
Conclusion
CRaC provides a viable alternative to GraalVM for achieving near‑instant Java startup in Kubernetes. It adds operational steps—creating and persisting snapshots—but gives developers another tool for optimizing cold‑start latency.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
