Deploy a Java App to Kubernetes
Containerize a Java app and deploy it to a local Kubernetes cluster.
Introduction
30 Second Summary
When an app crashes in production, someone has to notice, restart it, and hope users didn't leave. Teams that manage dozens or hundreds of services need a system that handles restarts, scaling, and updates automatically.
In this project, you will package a Java HTTP server as a Docker container image, deploy it to a local Kubernetes cluster, scale it to multiple replicas, watch it self-heal when a pod dies, and roll out a new version with zero downtime.
What You'll Build
You will visit http://localhost:8080 in your browser and see your Java app responding. Then you will kill one of its pods and refresh the page to prove the other replicas kept serving traffic without missing a beat.
By the end of this project, you'll have:
- A Java HTTP server running in your browser that you built from source, compiled, and packaged into a container image.
- A self-healing Kubernetes Deployment with 3 replicas where you can kill any pod, refresh the page, and see traffic still flowing while Kubernetes spins up a replacement.
- A zero-downtime rolling update from version 1 to version 2 of your app, deployed the same way production teams ship new releases.
- Secret Mission: Add a health check endpoint and configure Kubernetes liveness probes so unhealthy pods get restarted automatically.
Are there any prerequisites?
You need Docker Desktop installed, a Java 17 JDK installed, and a code editor (any editor works). Step 1 walks you through enabling Kubernetes inside Docker Desktop.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters. You'll take a Java app from source code all the way to a running, self-healing Kubernetes cluster, following the same container-to-cluster lifecycle your team uses to manage webMethods modules.
Set Up Kubernetes
You need a running Kubernetes cluster on your machine before you can deploy anything to it.
Docker Desktop includes a built-in Kubernetes cluster, but it is turned off by default. In this step, you will enable it, confirm your cluster is running, and create the project folder for your files.
In this step, get ready to:
- Enable Kubernetes in Docker Desktop.
- Verify that kubectl can communicate with your cluster.
- Create the project directory.
Enable Kubernetes in Docker Desktop
Docker Desktop ships with a single-node Kubernetes cluster that you can turn on with one checkbox. Once enabled, it pulls the necessary container images and starts the cluster in the background.
- Press Cmd+Space to open your search bar.
- Type Docker and press Enter to open Docker Desktop.
- Click the gear icon in the top-right corner to open Settings.
- Select the Kubernetes tab in the left sidebar.
- Check the box labeled Enable Kubernetes.
- Click Apply & Restart.
Docker Desktop now pulls around 10 container images to set up the cluster. This takes 1-2 minutes depending on your internet speed.
Wait until a green indicator appears in the bottom-left corner of Docker Desktop beside Kubernetes running. This confirms your cluster is operational.
Kubernetes indicator stays orange?
If the indicator does not turn green after 3-4 minutes, try restarting Docker Desktop. Click the Docker icon in your menu bar, select Restart, and wait for Kubernetes to reinitialize.
Make sure you have a stable internet connection. Docker Desktop needs to pull images from the internet the first time you enable Kubernetes.
Help me troubleshoot Kubernetes not starting in Docker Desktop
Verify kubectl and set context
When you enabled Kubernetes, Docker Desktop also installed kubectl at /usr/local/bin/kubectl. This is the command-line tool you use to talk to any Kubernetes cluster.
- Press Cmd+Space to open your search bar.
- Type Terminal and press Enter to open your terminal.
- Point kubectl at the Docker Desktop cluster by running this command:
kubectl config use-context docker-desktop
What does this command do?
Your machine can have multiple Kubernetes clusters configured (local, cloud, staging). kubectl config use-context tells kubectl which cluster to target. Here you are pointing it at the one built into Docker Desktop.
Before you run the next command, what do you think kubectl will report about your cluster?
- Verify the cluster is ready by running:
kubectl get nodes
You should see one node named docker-desktop with status Ready.
✔️ I see docker-desktop with status Ready
Your Kubernetes cluster is running and kubectl is communicating with it. You are ready to move on.
ⓧ I see NotReady or an error
If the node shows NotReady, the cluster is still initializing. Wait 30 seconds and run kubectl get nodes again.
If you see connection refused, confirm the green Kubernetes indicator is visible in Docker Desktop settings. If it is not, return to the previous substep and re-enable Kubernetes.
Help me fix kubectl not connecting to Docker Desktop Kubernetes
Create the project directory
You need a dedicated folder on your Desktop to hold all the files for this project.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir java-k8s-demo
cd java-k8s-demo
- Confirm you are in the correct directory by running:
pwd
You should see a path ending in java-k8s-demo, such as /Users/yourname/Desktop/java-k8s-demo.
Path doesn't end with java-k8s-demo?
Make sure you ran cd ~/Desktop before creating the folder. If the folder already exists from a previous attempt, just run cd ~/Desktop/java-k8s-demo to move into it.
Help me navigate to my project directory
Your local Kubernetes cluster is running and your project folder is ready. Next up, you will write a Java app, package it into a Docker image, and see it respond in your browser.
Build a Java App and Run It in Docker
Your Kubernetes cluster is up and running. Before you can deploy anything to it, you need something to deploy: a container image.
This step takes a simple Java app from source code to a running Docker container. This is the same unit of work your team packages each webMethods module into.
In this step, get ready to:
- Write a Java HTTP server and compile it.
- Package the compiled app into a Docker container image.
- Run the container and confirm the app responds in your browser.
Write the Java application
Java ships with a built-in HTTP server class called com.sun.net.httpserver.HttpServer. It handles incoming requests without any external dependencies, which keeps this project focused on containers and Kubernetes.
- Create a file called App.java inside your java-k8s-demo folder by pasting the following code into your editor:
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
public class App {
public static void main(String[] args) throws IOException {
// Create an HTTP server listening on port 8080
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/", new HttpHandler() {
@Override
public void handle(HttpExchange exchange) throws IOException {
// The response message your browser will display
String response = "Hello from Kubernetes! Module v1 running in a container.";
exchange.sendResponseHeaders(200, response.getBytes().length);
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
}
});
server.start();
System.out.println("Server running on port 8080");
}
}
What does this code do?
This is a minimal web server in a single file. Here is what each piece does:
- HttpServer.create(new InetSocketAddress(8080), 0) starts a server that listens for incoming connections on port 8080.
- server.createContext("/", ...) registers a handler that runs whenever someone visits the root URL.
- The handler sends back a plain-text message with a 200 OK status code.
- Save App.java.
- Compile the Java file into bytecode by running this command in your terminal:
javac App.java
- Confirm the compiled class file exists by running:
ls App.class
You should see App.class printed in the output. This is the compiled bytecode that will run inside your container.
Seeing 'javac: command not found'?
This means your Java JDK is not on your system path. Make sure you have Java 17 JDK installed (not just the JRE).
Verify your Java version by running java -version in your terminal. You need version 17 or higher.
help me fix javac not found on my machine
Write the Dockerfile
A Dockerfile is a recipe that tells Docker how to package your app into a container image. It specifies the base image, copies your files in, and defines how to start the app.
- Create a file called Dockerfile (no extension) in the same java-k8s-demo folder with the following content:
# Use a lightweight Java runtime as the base image
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# Copy the compiled Java class into the container
COPY App.class .
EXPOSE 8080
# Run the app when the container starts
CMD ["java", "App"]
What does this Dockerfile do?
- FROM eclipse-temurin:17-jre-alpine starts from a small (~65 MB) image that already has the Java 17 runtime installed.
- WORKDIR /app sets the working directory inside the container.
- COPY App.class . copies your compiled bytecode into the container.
- CMD ["java", "App"] tells Docker to run your app whenever the container starts.
- Save Dockerfile.
- Confirm both project files are in place by running:
ls
You should see App.java, App.class, and Dockerfile listed in the output.
Missing a file?
Make sure you are saving files into the java-k8s-demo directory. Run pwd to confirm your terminal is in the right folder.
Check that your Dockerfile has no file extension. Some editors add .txt automatically.
help me find my missing project files
Build and test the image
With your source code compiled and your Dockerfile ready, you can now build the container image. This packages everything into a single artifact that Kubernetes can run.
- Build the Docker image by running this command:
docker build -t java-k8s-demo:1.0 .
What does this command do?
- docker build reads your Dockerfile and executes each instruction to assemble the image.
- -t java-k8s-demo:1.0 names the image java-k8s-demo and tags it as version 1.0.
- The . at the end tells Docker to look for the Dockerfile in your current directory.
You should see Docker processing each layer and finishing with a success message that names your image.
Now for the real test. Before you run the container, what do you think will happen when you visit http://localhost:8080 in your browser?
- Start a container from your image by running:
docker run --rm -p 8080:8080 java-k8s-demo:1.0
What do these flags mean?
- --rm automatically removes the container when you stop it, keeping your system tidy.
- -p 8080:8080 maps port 8080 on your machine to port 8080 inside the container.
You should see Server running on port 8080 printed in your terminal. The container is now running and waiting for requests.
- Open your browser and visit http://localhost:8080.
You should see the message: "Hello from Kubernetes! Module v1 running in a container."
- Return to your terminal and press Ctrl+C to stop the container.
Browser showing 'unable to connect'?
Make sure your terminal still shows Server running on port 8080. If the process crashed, re-run the docker run command.
Check that nothing else is using port 8080. Run lsof -i :8080 to see if another process is occupying the port.
help me debug my Docker container not responding on localhost
✔️ Awesome, I've got everything!
Great. Make sure both App.java and Dockerfile are saved in your java-k8s-demo folder.
ⓧ I'd like to double check the full code
Here is App.java:
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
public class App {
public static void main(String[] args) throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/", new HttpHandler() {
@Override
public void handle(HttpExchange exchange) throws IOException {
String response = "Hello from Kubernetes! Module v1 running in a container.";
exchange.sendResponseHeaders(200, response.getBytes().length);
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
}
});
server.start();
System.out.println("Server running on port 8080");
}
}
Here is Dockerfile:
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY App.class .
EXPOSE 8080
CMD ["java", "App"]
Your Java app is now containerized and proven to work. Next up, you will hand this container off to Kubernetes and let the cluster manage it for you.
Deploy to Kubernetes
In the previous step, you built a Docker image and ran your Java app with docker run. That worked, but you had to start and stop the container yourself.
Kubernetes takes over that job. It keeps your app running, restarts it if it crashes, and routes traffic to it. In this step, you'll hand your container off to Kubernetes using a Deployment and a Service.
In this step, get ready to:
- Write a Deployment manifest to tell Kubernetes how to run your container.
- Write a Service manifest to expose your app to the browser.
- Apply both manifests and verify the app is served by Kubernetes.
Write the Deployment manifest
A Deployment is a Kubernetes resource that tells the cluster what container to run, how many copies (replicas) to keep alive, and how to label them for routing. You define it in a YAML file called a manifest.
- Create a new file called deployment.yaml in your java-k8s-demo folder with the following content:
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
spec:
replicas: 1
selector:
matchLabels:
app: java-app
template:
metadata:
labels:
app: java-app
spec:
containers:
- name: java-app
image: java-k8s-demo:1.0
ports:
- containerPort: 8080
imagePullPolicy: Never
What does this manifest do?
- replicas: 1 tells Kubernetes to run exactly one copy of your container.
- image: java-k8s-demo:1.0 points to the Docker image you built in the previous step.
- imagePullPolicy: Never tells Kubernetes to use the local image from Docker's cache instead of trying to pull it from a remote registry.
- The app: java-app label connects this Deployment to the Service you'll write next.
- Save deployment.yaml.
- Confirm the file exists by running:
ls deployment.yaml
You should see deployment.yaml listed in the output.
File not found?
- Make sure you saved the file inside the java-k8s-demo folder, not a parent directory.
- Check that the filename is exactly deployment.yaml with no extra extension like .txt.
help me troubleshoot why my deployment.yaml file is not found
✔️ Awesome, I've got everything!
Great. Make sure you've saved deployment.yaml and move on to the next file.
ⓧ I'd like to double check the full code
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
spec:
replicas: 1
selector:
matchLabels:
app: java-app
template:
metadata:
labels:
app: java-app
spec:
containers:
- name: java-app
image: java-k8s-demo:1.0
ports:
- containerPort: 8080
imagePullPolicy: Never
Write the Service manifest
A Service routes network traffic to your Pods. Without it, your app runs inside the cluster but has no way to receive requests from your browser.
- Create a new file called service.yaml in your java-k8s-demo folder with the following content:
apiVersion: v1
kind: Service
metadata:
name: java-app-service
spec:
type: LoadBalancer
selector:
app: java-app
ports:
- port: 8080
targetPort: 8080
What does this manifest do?
- type: LoadBalancer maps the Service to localhost on Docker Desktop, making it accessible in your browser.
- selector: app: java-app matches the label on your Deployment's pods, so traffic is routed to the right containers.
- port: 8080 is the port you visit. targetPort: 8080 is the port your Java app listens on inside the container.
- Save service.yaml.
- Confirm both manifest files exist by running:
ls *.yaml
You should see both deployment.yaml and service.yaml listed.
Only seeing one file?
- Check that you saved service.yaml in the same java-k8s-demo folder as deployment.yaml.
- Run pwd to verify your terminal is inside java-k8s-demo.
help me find my missing yaml file
✔️ Awesome, I've got everything!
Great. Make sure you've saved service.yaml and move on to deploying.
ⓧ I'd like to double check the full code
apiVersion: v1
kind: Service
metadata:
name: java-app-service
spec:
type: LoadBalancer
selector:
app: java-app
ports:
- port: 8080
targetPort: 8080
Deploy and verify
Both manifests are ready. Now you'll hand them to Kubernetes and watch it create the Deployment, start a pod, and expose the Service.
- Apply both manifests by running this command:
kubectl apply -f deployment.yaml -f service.yaml
What does kubectl apply do?
kubectl apply -f reads a YAML manifest and creates the resources it defines in your cluster. Passing both files at once creates the Deployment and Service together.
You should see output confirming both resources were created:
deployment.apps/java-app created
service/java-app-service created
- Check that your pod is running:
kubectl get pods
You should see one pod with Running in the STATUS column.
Before you check the browser, what do you think you'll see at http://localhost:8080? Will it be the same response you saw with docker run, or something different?
- Open your browser and visit http://localhost:8080.
You should see: "Hello from Kubernetes! Module v1 running in a container."
The response is identical to what you saw with docker run. The difference is that Kubernetes is now managing the container for you. If the pod crashes, Kubernetes will restart it automatically.
Not seeing the response in your browser?
- Run kubectl get pods and check that the STATUS column shows Running. If it shows ErrImagePull or ImagePullBackOff, your imagePullPolicy is not set to Never. Open deployment.yaml and add imagePullPolicy: Never under the image: line, then re-apply with kubectl apply -f deployment.yaml.
- If the pod is Running but the browser shows nothing, wait 10 seconds and refresh. The LoadBalancer Service can take a moment to map to localhost.
help me troubleshoot my Kubernetes deployment
Your app is now running on Kubernetes. But with only one replica, if that single pod dies, there's a brief outage. Next up, you'll scale to multiple replicas and see Kubernetes self-heal in real time.
Scale and Self-Heal
Your Java app is running on Kubernetes and serving traffic at http://localhost:8080. But right now, a single pod is doing all the work.
With only 1 replica, your app has a single point of failure. If that Pod dies, there is a brief outage before Kubernetes replaces it. This is the same risk your team avoids by running multiple instances of critical webMethods modules.
In this step, get ready to:
- Kill your single pod and experience the downtime firsthand.
- Scale to 3 replicas to eliminate the single point of failure.
- Kill a pod again and observe Kubernetes self-heal with zero downtime.
Kill the single pod and observe the outage
Right now your Deployment runs a single pod. What happens if that pod disappears? Instead of guessing, you are going to find out directly.
- Find your pod name by running:
kubectl get pods
You will see output with a NAME column showing something like java-app-xxxxx-xxxxx. Copy that full pod name.
Before you delete this pod, what do you think will happen when you visit http://localhost:8080 immediately after the pod is gone?
- Delete the pod by running:
kubectl delete pod [[PODNAME="your-pod-name-from-above"]]
- Immediately try to access your app by running:
curl http://localhost:8080
The request may fail or hang briefly. Your app is down because Kubernetes has no other pods to route traffic to.
- Check what Kubernetes is doing behind the scenes by running:
kubectl get pods
You will see a new pod starting up as a replacement. Kubernetes detected the missing pod and is already creating a new one to match the desired state of 1 replica.
Why did the app go down?
With only 1 replica, there was no backup pod to handle requests while Kubernetes spun up a replacement. The gap between the old pod dying and the new pod becoming ready is the outage window.
The fix is simple: run more replicas so that healthy pods absorb traffic while replacements start.
Scale to 3 replicas
Now that you have felt the single-pod outage firsthand, the solution is to run multiple replicas. Kubernetes will distribute traffic across all healthy pods, so losing one no longer causes downtime.
- Scale your Deployment to 3 replicas by running:
kubectl scale deployment java-app --replicas=3
- Confirm all 3 pods are running:
kubectl get pods
You should see 3 pods listed, each with status Running. All three are serving your app at http://localhost:8080.
What does scaling do?
The kubectl scale command updates the Deployment's desired replica count. Kubernetes then creates new pods until the actual count matches the desired count.
The Service automatically routes traffic to all pods matching its selector label. No configuration change is needed on the Service side.
Pods stuck in Pending?
If a pod stays in Pending status, your Docker Desktop may not have enough memory allocated. Open Docker Desktop, go to Settings, then Resources, and ensure at least 4 GB of memory is available.
Help me figure out why my pods are stuck in Pending
Kill a pod again and observe self-healing
With 3 replicas running, the same failure scenario should now have a completely different outcome.
- Find a pod name to delete by running:
kubectl get pods
This time, what do you think will happen when you delete one of these 3 pods? Will the app go down again?
- Delete any one of the pods by running:
kubectl delete pod [[PODNAME="any-pod-name-from-above"]]
- Immediately test whether your app is still responding:
curl http://localhost:8080
The app still responds. The other 2 pods absorbed the traffic without missing a beat.
- Check the pod list to see self-healing in action:
kubectl get pods
You will see a brand-new pod starting up (or already running) alongside the 2 that never went down. Kubernetes maintains the desired count of 3 replicas automatically.
What is self-healing?
Kubernetes continuously compares the actual state (how many pods are running) with the desired state (the replica count you specified). When a pod dies, the gap triggers an automatic replacement.
This is why your team runs multiple replicas of critical webMethods modules. Self-healing means no one needs to wake up at 2 AM to restart a crashed process.
App not responding after deleting a pod?
Run kubectl get pods and check that at least 2 pods still show Running status. If all pods were somehow deleted, re-run kubectl scale deployment java-app --replicas=3 and wait for them to reach Running status.
Help me debug why my app is not responding after pod deletion
Your app now survives pod failures without any downtime. Next up, you will ship a new version of your app using a rolling update, replacing v1 with v2 while keeping the app online the entire time.
Roll Out an Update
Your app now runs across 3 replicas, and you have seen Kubernetes automatically replace killed pods.
Now it is time to ship a new version. Your team pushes updated webMethods modules regularly, and Kubernetes performs rolling updates so the old version keeps serving traffic while new pods start. You will update your app, rebuild the image, and let Kubernetes swap versions with zero downtime.
In this step, get ready to:
- Update the Java app source code and rebuild the container image.
- Tell Kubernetes to use the new image with a rolling update.
- Verify the new version is live with zero downtime.
Update the Java app and rebuild
The response message in your app currently says version 1. You will change it to version 2, then recompile and package a fresh container image.
- In your code editor, open App.java.
- Find this line inside the handle method:
String response = "Hello from Kubernetes! Module v1 running in a container.";
- Replace it with the v2 message, so the line now looks like this:
String response = "Hello from Kubernetes! Module v2 deployed with zero downtime.";
- Save App.java.
- Recompile and build a new Docker image tagged 2.0 by running these commands:
javac App.java
docker build -t java-k8s-demo:2.0 .
What do these commands do?
javac App.java compiles your updated source into a fresh App.class bytecode file.
docker build -t java-k8s-demo:2.0 . packages that new class file into a container image. The :2.0 tag distinguishes it from the :1.0 image your cluster currently runs.
You should see Docker output ending with a line tagging your new image as java-k8s-demo:2.0.
Seeing a compile error?
Make sure the new string is surrounded by double quotes and the line ends with a semicolon. The rest of the file should be unchanged.
Help me fix a compile error in my App.java file after changing the response string.
✔️ Awesome, I've got everything!
Great. Double check you have saved App.java with the new message.
ⓧ I'd like to double check the full code
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
public class App {
public static void main(String[] args) throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/", new HttpHandler() {
@Override
public void handle(HttpExchange exchange) throws IOException {
String response = "Hello from Kubernetes! Module v2 deployed with zero downtime.";
exchange.sendResponseHeaders(200, response.getBytes().length);
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
}
});
server.start();
System.out.println("Server running on port 8080");
}
}
Update the Deployment
Your new image exists locally but Kubernetes is still running the old one. A single command tells Kubernetes to swap images across all pods using a rolling update.
- Tell Kubernetes to use the new image by running:
kubectl set image deployment/java-app java-app=java-k8s-demo:2.0
What does this command do?
kubectl set image updates the container image for a running Deployment. Kubernetes does not restart all pods at once. It creates a new pod with the 2.0 image, waits for it to be ready, then terminates an old pod. This repeats until all replicas run the new version.
Traffic keeps flowing to the remaining old pods during the swap, so users never see downtime.
- Watch the rollout progress by running:
kubectl rollout status deployment/java-app
You should see the message deployment "java-app" successfully rolled out once all pods are updated.
Rollout seems stuck?
If the rollout hangs, the most common cause is the image name or tag being incorrect. Run docker images and confirm you see java-k8s-demo with tag 2.0 in the list.
Help me debug a stuck Kubernetes rolling update for my java-app deployment.
Now that the live cluster is updated, update your deployment.yaml file to match. This keeps your manifest in sync so you can re-apply it in the future without drifting back to the old version.
- Replace the entire contents of deployment.yaml with the following:
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
spec:
replicas: 3
selector:
matchLabels:
app: java-app
template:
metadata:
labels:
app: java-app
spec:
containers:
- name: java-app
image: java-k8s-demo:2.0
ports:
- containerPort: 8080
imagePullPolicy: Never
What changed in the manifest?
Two values changed. replicas: 3 reflects the scaling you did in the previous step (it was 1 before). image: java-k8s-demo:2.0 matches the rolling update you just performed.
Keeping manifest files up to date means you can always recreate the exact state of your cluster from code.
- Save deployment.yaml.
Verify the update
Time to confirm the new version is live. Before you check, think about what you expect to see at http://localhost:8080 now.
- Open your browser and visit http://localhost:8080.
You should see the updated message: Hello from Kubernetes! Module v2 deployed with zero downtime..
- Confirm all pods are running the new version by running:
kubectl get pods
You should see 3 pods with fresh names and a young AGE value. These are the replacement pods Kubernetes created during the rolling update.
Still seeing the v1 message?
Try a hard refresh in your browser (Cmd+Shift+R on macOS or Ctrl+Shift+R on Windows). Cached responses can show stale content.
If that does not fix it, run kubectl rollout status deployment/java-app again to confirm the rollout finished.
Help me figure out why my browser still shows the v1 message after a rolling update.
Secret mission
Add a Liveness Probe
Kubernetes can restart pods automatically when they become unhealthy. But right now, your cluster has no way to know if your app is actually responsive. In this secret mission, you will add a health check endpoint to your Java app and configure Kubernetes liveness probes so unresponsive pods get restarted without human intervention.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely on your local machine, so there are no ongoing cloud costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- Kubernetes Deployment java-app (3 replicas running on your local cluster).
- Kubernetes Service java-app-service (LoadBalancer routing traffic to your pods).
- Docker images java-k8s-demo:1.0, java-k8s-demo:2.0, and java-k8s-demo:3.0 stored in your local Docker daemon.
- Local project directory java-k8s-demo on your Desktop.
Keep everything running
No action needed. Choose this if you are still actively building or experimenting with your Kubernetes deployment.
- Your 3 pods continue running and serving traffic at http://localhost:8080.
- Docker Desktop continues running Kubernetes in the background, using some memory and CPU.
- Your Docker images remain stored locally, taking up disk space (roughly 65 MB each for the Alpine-based images).
Pause - I'll come back to this later
Shut down your Kubernetes workloads to free up memory and CPU, while keeping your files and images intact for later.
- Scale your deployment to zero replicas by running:
kubectl scale deployment java-app --replicas=0
- This stops all pods without deleting the Deployment or Service definitions.
- When you are ready to resume, scale back up by running:
kubectl scale deployment java-app --replicas=3
- Your project files and Docker images remain on disk, ready to use.
Delete - I don't want to use this again
Remove all project resources and start fresh. Follow each step below to fully clean up.
- Delete the Kubernetes Deployment and Service by running:
kubectl delete deployment java-app
kubectl delete service java-app-service
- Remove the Docker images by running:
docker rmi java-k8s-demo:1.0 java-k8s-demo:2.0 java-k8s-demo:3.0
- Delete the local project directory by running:
rm -rf ~/Desktop/java-k8s-demo
- Optionally, disable Kubernetes in Docker Desktop. Open Docker Desktop, go to Settings, select the Kubernetes tab, uncheck Enable Kubernetes, and click Apply & Restart. This frees the memory used by the local cluster.
That's a Wrap!
That's a Wrap!
Nice work! You just took a Java application from source code all the way to a production-style Kubernetes deployment with multiple replicas, self-healing, and zero-downtime updates.
You've learned how to:
- Built a Java HTTP server and packaged it as a Docker container image using Eclipse Temurin 17.
- Deployed to Kubernetes with 3 replicas that self-heal when pods are killed, eliminating single points of failure.
- Performed a rolling update from v1 to v2 with zero downtime, the same pattern used to ship new versions of production services.
- Secret Mission: Configured a liveness probe for automatic health monitoring and pod restart.
Ready to quiz yourself?