DEV Community

Cover image for Running Java Applications on Kubernetes with MicroK8s in WSL2: A Complete Step-by-Step Guide
Sanjay Ghosh
Sanjay Ghosh

Posted on • Edited on

Running Java Applications on Kubernetes with MicroK8s in WSL2: A Complete Step-by-Step Guide

šŸš€ Introduction

Deploying Kubernetes locally can feel like a breeze—until you introduce cross-OS boundaries like running MicroK8s inside an Ubuntu WSL2 instance on a Windows machine. Suddenly, networking bridges, cluster security templates, and isolated container image caches clash.

In this guide, I will take you through my entire journey from absolute scratch: initializing the cluster, bypassing the Windows-to-WSL network layer, containerizing a legacy .war file into Apache Tomcat 10, resolving local container runtime blocks, and scaling to a load-balanced multi-pod deployment.


Overall Flow :

Deployment Flow:

šŸ› ļø Phase 1: Installing and Configuring the Cluster

MicroK8s is Canonical's lightweight, fast-booting Kubernetes distribution that works natively on Ubuntu via Snap packages.

1. Installation & Init

Run the standard package manager to pull the classic snap layer down and wait for readiness flags to clear:

sudo snap install microk8s --classic
sudo microk8s status --wait-ready
sudo microk8s enable dns dashboard storage
Enter fullscreen mode Exit fullscreen mode

2. The WSL2 Network Barrier šŸ›‘

Because the cluster builds internal authorization certificates tied exclusively to the Linux environment, a Windows web browser attempting to view the dashboard on localhost will be strictly blocked. We must patch the cluster's trusted certificate schema.

First, check your active Ubuntu network IP address:

ip route
## Look for your eth0 source IP (e.g., 172.21.236.75)
Enter fullscreen mode Exit fullscreen mode

Open the root certificate mapping file:

sudo vi /var/snap/microk8s/current/certs/csr.conf.template
Enter fullscreen mode Exit fullscreen mode

Scroll down to the [ alt_names ] segment and explicitly append your Ubuntu network address to the safe-list:

IP.3 = 172.21.236.75
Enter fullscreen mode Exit fullscreen mode

Force a full service recycle to re-mint the secure tokens:

sudo microk8s stop && sudo microk8s start
Enter fullscreen mode Exit fullscreen mode

3. Launching the Security Proxy

Extract your cluster access token:

sudo microk8s kubectl describe secret -n kube-system microk8s-dashboard-token
Enter fullscreen mode Exit fullscreen mode

Spin up the connection bridge utility to forward cluster resources through to your host:

sudo microk8s dashboard-proxy
Enter fullscreen mode Exit fullscreen mode

Open your Windows web browser, navigate to https://127.0.0.1:10443, bypass the browser certificate safety warnings, and paste your authorization token. You're in!


šŸ“¦ Phase 2: Containerizing Your Java Web App

Kubernetes does not manage code files directly; it orchestrates containers. We need to wrap your compiled application file into an official application server baseline.

1. Establish Your Workspace

Create a folder structure inside Ubuntu to hold your codebase assets and copy your file over:

mkdir -p /mnt/c/MyDomain/TECH/kubernetes/allwork/kubernetes-java-app
cd /mnt/c/MyDomain/TECH/kubernetes/allwork/kubernetes-java-app
Enter fullscreen mode Exit fullscreen mode

2. Crafting the Dockerfile Blueprint

Create a raw text blueprint named Dockerfile to map out the Tomcat 10 installation parameters:

## Use official Tomcat 10 with Java 17 as the base foundation
FROM docker.io/tomcat:10.1-jre17

## Clean out the default placeholder webapps to prevent conflicts
RUN rm -rf /usr/local/tomcat/webapps/*

## Copy your local WAR file from your directory into Tomcat's deployment folder
COPY mywebprojectwar.war /usr/local/tomcat/webapps/

## Inform the container environment to expose Tomcat's web port
EXPOSE 8080
Enter fullscreen mode Exit fullscreen mode

3. Compiling the Image Package

Because MicroK8s focuses purely on execution workloads, use buildah to safely handle compilation layers on Ubuntu:

sudo apt update && sudo apt install -y buildah
sudo buildah bud -t my-java-app:v1 .
Enter fullscreen mode Exit fullscreen mode

🧬 Phase 3: The Local Sideloading Blueprint

If you try to run your containers right now, you will hit an immediate wall: ErrImageNeverPull. MicroK8s isolates local terminal compiles away from the live Kubernetes runtime container pools (k8s.io namespace).

Here is the exact side-loading injection pattern that guarantees a perfect local import:

1. Build a Standard Archive

Export the image into an unmanaged, standardized archive on your hard drive:

rm -f my-java-app.tar
sudo buildah push my-java-app:v1 docker-archive:my-java-app.tar:my-java-app:v1
Enter fullscreen mode Exit fullscreen mode

2. Stream into Cluster Core Memory

Use the high-level MicroK8s packaging command along with a file stream director (<) to automatically unpack the elements straight into the cluster namespace:

sudo microk8s images import < my-java-app.tar
Enter fullscreen mode Exit fullscreen mode

3. Verify Your Cache Identifiers

Query the exact internal text string that the cluster engine assigned to your package:

sudo microk8s ctr --namespace=k8s.io image list | grep managed
Enter fullscreen mode Exit fullscreen mode

You will notice your file carries the string tag localhost/my-java-app:v1 bound to a specific runtime permission marker (io.cri-containerd.image=managed).


šŸŽ¼ Phase 4: Defining the Orchestration Layout (YAML)

Now we write the declarative automation file. We want 2 separate copies (replicas) of Tomcat spinning up simultaneously, sitting safely behind a single load-balancing endpoint.

Create a file named app-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: java-app-deployment
  labels:
    app: my-java-web-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-java-web-app
  template:
    metadata:
      labels:
        app: my-java-web-app
    spec:
      containers:
      - name: tomcat-container
        image: localhost/my-java-app:v1
        imagePullPolicy: Never # Forces K8s to look only at your hard drive cache
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: java-app-service
spec:
  type: ClusterIP
  selector:
    app: my-java-web-app
  ports:
  - port: 8080
    targetPort: 8080
Enter fullscreen mode Exit fullscreen mode

Fire up the structural blueprints into the cluster engine:

sudo microk8s kubectl apply -f app-deployment.yaml
Enter fullscreen mode Exit fullscreen mode

šŸ•µļø Phase 5: Verification & Stepping Inside the Matrix

Track your applications as they boot up live into active server spaces:

sudo microk8s kubectl get pods -w
Enter fullscreen mode Exit fullscreen mode

When successful, your console will print:

NAME                                   READY   STATUS    RESTARTS   AGE
java-app-deployment-56fd4d8d5f-ddd4n   1/1     Running   0          10s
java-app-deployment-56fd4d8d5f-n69wp   1/1     Running   0          10s
Enter fullscreen mode Exit fullscreen mode

Stepping Inside the Pod Environments 🤯

Are those Tomcats really there? Let's physically step inside the isolated filesystem of each individual running node to verify:

## Step inside Pod 1
sudo microk8s kubectl exec -it java-app-deployment-56fd4d8d5f-ddd4n -- bash
ls -la /usr/local/tomcat/webapps
exit

## Step inside Pod 2
sudo microk8s kubectl exec -it java-app-deployment-56fd4d8d5f-n69wp -- bash
ls -la /usr/local/tomcat/webapps
exit
Enter fullscreen mode Exit fullscreen mode

Each container runs its own isolated filesystem partition with independent copies of Tomcat 10 and your web project!


🌐 Phase 6: Routing Live Traffic to Windows

To view your load-balanced environment, map an open port on your host computer (like 9595) directly to your front-end Kubernetes service router:

sudo microk8s kubectl port-forward service/java-app-service 9595:8080
Enter fullscreen mode Exit fullscreen mode

Keep that terminal active, switch to your Windows web browser, and access your custom project page:

http://localhost:9595/mywebprojectwar/index.jsp
Enter fullscreen mode Exit fullscreen mode

Every click and refresh on that web address is now automatically round-robinned and load-balanced across your 2 independent Tomcat containers!


🧼 Phase 7: Leaving No Trace (The Clean Slate)

When you are done experimenting and want to reclaim your system RAM, CPU cycles, and disk blocks back to a "Zero Stage" without breaking your machine, use this cleanup checklist:

## 1. Drop Applications
sudo microk8s kubectl delete -f app-deployment.yaml
sudo microk8s kubectl delete service my-app-kubernetes 2>/dev/null
sudo microk8s kubectl delete deployment my-app-kubernetes 2>/dev/null

## 2. Reclaim Image Storage Space
rm -f ~/kubernetes-java-app/*.tar
sudo buildah rmi --all
sudo microk8s ctr --namespace=k8s.io image rm $(sudo microk8s ctr --namespace=k8s.io image ls -q) 2>/dev/null

## 3. Shutdown Subsystems
sudo microk8s disable dashboard registry
sudo microk8s stop

## 4. Factory Reset (Optional)
sudo snap remove microk8s --purge && sudo snap install microk8s --classic
Enter fullscreen mode Exit fullscreen mode

šŸŽÆ Conclusion

Running Kubernetes inside WSL2 is a great way to build hands-on experience without needing cloud infrastructure or multiple virtual machines. While the setup introduces a few unique challenges—certificate configuration, image management, and networking—each issue has a straightforward solution once you understand how the pieces fit together.

In this guide, we built a complete local Kubernetes environment from scratch, containerized a Java web application using Buildah, deployed it on MicroK8s, scaled it with multiple replicas, and exposed it through a Kubernetes Service.

More importantly, we explored why each step was necessary rather than simply copying commands. Understanding concepts such as container runtimes, image namespaces, deployments, services, and port forwarding makes it much easier to troubleshoot real Kubernetes environments.

If you're beginning your Kubernetes journey, I hope this walkthrough saves you the hours of trial and error that I went through.

Top comments (0)