Ephemeral VM infrastructure with web-based VNC access - perfect for workshops, training sessions, and lab environments.
Provides 20 concurrent students with full Ubuntu desktop environments accessible through a web browser. No VNC client installation needed. Each student gets an isolated, fresh VM that resets after use.
Use Cases:
- Training workshops
- Coding bootcamps
- University lab sessions
- Hands-on demonstrations
- Testing environments
┌─────────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Golden Image (ubuntu-golden-rootdisk PVC) │ │
│ │ 📦 Ubuntu 24.04 + Desktop + Firefox + Tools (20GB) │ │
│ │ ✓ Created once, shared by all VMs │ │
│ │ ✓ Read-only base layer │ │
│ └────────────┬─────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────┴────────┬──────────┬──────────┬─────────────┐ │
│ ▼ ▼ ▼ ▼ ▼ │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ VM 1 │ │ VM 2 │ │ VM 3 │ │ ... │ │ VM20 │ │
│ │student1│ │student2│ │student3│ │ │ │student20│ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ 📝 Overlay │ 📝 Overlay│📝 Overlay│ ... │📝 Overlay│ │
│ │ (ephemeral) │ (ephemeral)│(ephemeral)│ │(ephemeral)│ │
│ │ 2-5GB │ 2-5GB │ 2-5GB │ │ 2-5GB │ │
│ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ │
│ │ │ │ │ │ │
│ │ KubeVirt │ │ │ │ │
│ │ VNC API │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ VNC │ │ VNC │ │ VNC │ │ VNC │ │
│ │Proxy │ │Proxy │ │Proxy │ ... │Proxy │ │
│ │ Pod │ │ Pod │ │ Pod │ │ Pod │ │
│ │ │ │ │ │ │ │ │ │
│ │virtctl│ │virtctl│ │virtctl│ │virtctl│ │
│ │websockify│ │websockify│websockify│ │websockify││
│ │noVNC │ │noVNC │ │noVNC │ │noVNC │ │
│ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ │
│ │ │ │ │ │
└─────┼────────────────┼──────────┼───────────────────────┼─────────┘
│ :30080 │ :30081 │ :30082 :30099 │
│ NodePort │ NodePort │ NodePort NodePort│
▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│Browser │ │Browser │ │Browser │ │Browser │
│Student1│ │Student2│ │Student3│ │Student20│
└────────┘ └────────┘ └────────┘ └────────┘
👤 👤 👤 👤
- Single PVC containing fully configured Ubuntu desktop
- Created once, shared by all VMs
- Read-only to prevent corruption
- Contains: OS + Desktop Environment + Applications
┌─────────────────────────────────────────────┐
│ Student creates file "homework.txt" │
│ ↓ │
│ Writes to student1's overlay only │
│ ↓ │
│ student2 cannot see this file │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Student reads /usr/bin/firefox │
│ ↓ │
│ Check overlay → Not found │
│ ↓ │
│ Read from golden image → Return binary │
└─────────────────────────────────────────────┘
Benefits:
- ✅ Each VM is isolated
- ✅ Changes are temporary (ephemeral)
- ✅ Delete VM → fresh start
- ✅ Storage efficient (20GB + 20×3GB = 80GB, not 400GB)
Student opens browser
↓
http://node-ip:30080/vnc.html
↓
NodePort Service (30080)
↓
VNC Proxy Pod
↓
┌───────────────────────────┐
│ 1. virtctl vnc │ ← Connects to KubeVirt API
│ --proxy-only │ ← Gets VNC stream from VM
│ :5900 │ ← Creates local VNC proxy
│ │
│ 2. websockify │ ← Converts VNC → WebSocket
│ :8080 → :5900 │ ← Wraps VNC for browser
│ │
│ 3. noVNC (HTML5 client) │ ← Renders in browser
│ /vnc.html │ ← No client install needed
└───────────────────────────┘
↓
KubeVirt VNC API
↓
VM's VNC Server (in guest OS)
↓
Desktop Display
Why this architecture?
- ❌ VMs don't expose VNC on network (security)
- ✅ KubeVirt provides VNC via API (authenticated)
- ✅ Proxy pod handles protocol conversion
- ✅ Browser-native experience (no VNC client)
Traditional Approach:
20 VMs × 20GB each = 400GB total
Our Approach:
┌──────────────────────────────────────┐
│ Golden Image: 20GB (shared) │
│ VM 1 Overlay: 3GB │
│ VM 2 Overlay: 3GB │
│ ... │
│ VM 20 Overlay: 3GB │
│ ───────────────────────────────── │
│ Total: ~70GB │
└──────────────────────────────────────┘
💾 Space Saved: 82%
Per VM:
├─ CPU: 2 cores
├─ RAM: 2GB
└─ Disk: ~3GB (overlay) + shared 20GB base
For 20 VMs:
├─ CPU: 40 cores
├─ RAM: 40GB
└─ Disk: 70GB
NodePort Range: 30080-30099
├─ student1 → :30080
├─ student2 → :30081
├─ student3 → :30082
│ ...
└─ student20 → :30099
- Kubernetes cluster (1.24+)
- KubeVirt installed
- Docker or container runtime
# See all available commands
make help
# Build VNC proxy image
make build-vnc
# Create golden image (one-time setup)
make create-golden-dv
make install-desktop
# Wait for cloud-init to complete (~10 min)
make finalize-golden
# Create and deploy VMs
make create-vms NUM_STUDENTS=20
make deploy-proxies
# Get access URLs
make urls# 1. Clone repository
git clone <repo-url>
cd kubevirt-lab
# 2. Build VNC proxy image
docker build -t vnc-proxy:v1 .
# 3. Create golden image (one-time, ~10 minutes)
kubectl apply -f golden-image-dv.yaml
kubectl wait --for=condition=Ready dv/ubuntu-golden-rootdisk --timeout=15m
# 4. Create 20 VMs
for i in {1..20}; do
sed "s/STUDENT_NAME/student$i/g; s/GOLDEN_IMAGE_NAME/ubuntu-golden-rootdisk/g" student-vm-template.yaml | kubectl apply -f -
done
# 5. Deploy VNC proxies
./deploy-vnc-proxies.sh allShare with students:
http://<your-node-ip>:30080/vnc.html (student1)
http://<your-node-ip>:30081/vnc.html (student2)
...
Login: student / ubuntu
┌─────────────────────────────────────────────────────┐
│ │
│ [CREATE] → [RUNNING] → [STOPPED] → [DELETED] │
│ ↑ ↓ │
│ └───────── [RESET] ─────┘ │
│ │
│ When VM deleted: │
│ ✓ Overlay deleted (ephemeral storage) │
│ ✓ Golden image untouched │
│ ✓ Next boot is fresh │
│ │
└─────────────────────────────────────────────────────┘
Start a session:
kubectl apply -f vm-template.yaml # Creates VM
./deploy-vnc-proxies.sh student1 # Exposes VNCEnd a session (reset VM):
kubectl delete vm student1 # Deletes VM + overlay
# Student gets fresh VM on next createUpdate golden image:
# 1. Stop all VMs
kubectl delete vm --all
# 2. Create temporary VM from golden image
# 3. Install updates/software
# 4. Stop temporary VM
# 5. Recreate golden image from updated diskScale to more students:
# Edit deploy-vnc-proxies.sh START_PORT if needed
for i in {21..40}; do
sed "s/student1/student$i/g" vm-template.yaml | kubectl apply -f -
done
./deploy-vnc-proxies.sh allFile: golden-image-dv.yaml
Purpose: Download and prepare base Ubuntu disk
Creates: PVC with Ubuntu 24.04 + Desktop
Size: 10-20GB
Build Time: 5-10 minutesFile: vm-template.yaml
Purpose: Define VM configuration
Disk Mode: Ephemeral (references golden PVC)
Cloud-init: Sets hostname, user, passwordFiles: Dockerfile, start-vnc-proxy.sh
Purpose: Provide browser-based VNC access
Contains:
- virtctl (KubeVirt CLI)
- websockify (VNC→WebSocket converter)
- noVNC (HTML5 VNC client)File: deploy-vnc-proxies.sh
Purpose: Deploy VNC proxies for all VMs
Creates:
- RBAC (ServiceAccount, ClusterRole)
- Service (NodePort per VM)
- Deployment (Proxy pod per VM)Edit vm-template.yaml:
resources:
requests:
memory: 2Gi # Increase for heavy workloads
cpu:
cores: 2 # Increase for compute-intensive tasksEdit vm-template.yaml cloud-init section:
users:
- name: student
plain_text_passwd: ubuntu # Change password hereEdit deploy-vnc-proxies.sh:
START_PORT=30080 # Change to different rangeEdit golden-image-dv.yaml or create VM, install software, export disk.
- ✅ VMs isolated from each other (network namespaces)
- ✅ VNC not exposed directly (API-based access)
- ✅ RBAC controls API access
- ✅ NodePorts can be firewalled to internal network
⚠️ Default password (change in production)⚠️ No encryption on VNC stream (use ingress with TLS)
Production recommendations:
- Use Ingress with TLS for HTTPS access
- Implement authentication (OAuth2, LDAP)
- Change default passwords
- Use NetworkPolicies for VM isolation
- Enable audit logging
# Check VM status
kubectl get vm,vmi
# Check VNC proxy status
kubectl get pods | grep vnc
# View VM console
virtctl console student1
# View proxy logs
kubectl logs -l app=vnc-student1
# Check resource usage
kubectl top nodes
kubectl top podskubectl describe vmi student1
# Look for: Image pull errors, resource constraints# Check proxy pod
kubectl logs -l app=vnc-student1
# Check service
kubectl get svc vnc-student1
# Test from inside cluster
kubectl run test --rm -it --image=curlimages/curl -- curl http://vnc-student1:8080/vnc.html# Check cloud-init completed
virtctl console student1
sudo cloud-init status --long
# Check disk space
df -h- KubeVirt Documentation
- CDI Documentation
- noVNC Project
- Setup Guide - Detailed installation steps