KubeKosh runs a real K3s Kubernetes cluster inside a single Docker container and pairs it with a browser-based terminal and automated scenario validation — no cloud account or local cluster required.
Screenshots
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Quick Start
Prerequisite: Docker
docker run -itd --name kubekosh --privileged -p 7554:80 zeborg/kubekosh:latest
Open http://localhost:7554 — wait ~30 seconds for the Cluster Ready indicator to turn green.
--privilegedis required — K3s needs access to kernel namespaces and cgroups.
Security Warning: Do not expose this container publicly. Use it only on your local machine as it is meant for educational purposes only.
Persist Progress
docker run -itd --name kubekosh --privileged -p 7554:80 \ -v <your_custom_directory>:/data zeborg/kubekosh:latest
Progress is stored in SQLite at /data/progress.db inside the container. Mount a local directory to /data to keep progress across container restarts.
Build From Source
docker build -t kubekosh . # multi-platform docker buildx build --platform linux/amd64,linux/arm64 -t kubekosh .
What's Inside
| Bundle | Focus | Exam Mode |
|---|---|---|
| 🌱 Kubernetes Basics | Core concepts | 60 min |
| 🧑✈️ Kubernetes Administrator | CKA | 120 min |
| 🛠️ Kubernetes Developer | CKAD | 120 min |
| 🛡️ Kubernetes Security | CKS | 120 min |
Scenario types:
- Task — Hands-on challenge in the live terminal. Click Validate for automated cluster-state checking.
- MCQ — Multiple-choice question with a detailed explanation on submission.
Shell Aliases
The terminal comes pre-configured with:
| Alias | Expands to |
|---|---|
k |
kubectl |
kg |
kubectl get |
kd |
kubectl describe |
krm |
kubectl delete |
kgp |
kubectl get pods |
kga |
kubectl get pods --all-namespaces |
kgd |
kubectl get deployments |
kgs |
kubectl get services |
kgn |
kubectl get nodes |
kgns |
kubectl get namespaces |
kdp |
kubectl describe pod |
kaf |
kubectl apply -f |
kdf |
kubectl delete -f |
kex |
kubectl exec -it |
klogs |
kubectl logs |
kns <ns> |
kubectl config set-context --current --namespace=<ns> |
kctx <ctx> |
kubectl config use-context <ctx> |
Architecture
| Component | Technology |
|---|---|
| Frontend | React + Vite, xterm.js |
| Backend | Node.js / Express, node-pty WebSocket PTY |
| Cluster | K3s (single-node, in-container) |
| Proxy | nginx on container port 80, mapped to host port 7554 |
| Storage | SQLite (better-sqlite3) at /data/progress.db |
Everything runs inside a single Docker image managed by scripts/entrypoint.sh.
Repository Layout
scenarios/
├── data/ # One JSON file per scenario -> <scenario-id>.json
├── bundles/ # One JSON file per bundle -> <bundle-id>.json
└── SCHEMA.md # Full schema reference
backend/
└── server.js # Express API + WebSocket PTY
frontend/
└── src/ # React + Vite SPA
scripts/
├── entrypoint.sh # Container startup (k3s -> API -> nginx)
└── nginx.conf # Reverse-proxy config
Contributing
Contributions are what make open-source projects like this one grow — and every contribution counts, big or small. Whether you're fixing a typo, polishing a scenario description, or building a completely new exercise from scratch, you're helping the next person learn Kubernetes in the best way possible. Thank you for taking the time!
Adding Scenarios
Each scenario is a single JSON file in scenarios/data directory; each bundle is a single JSON file in scenarios/bundles directory. See scenarios/SCHEMA.md for the full schema.
Task checklist:
validation.commands— idempotentkubectlcommands onlysetup_commands/teardown_commands—kubectlor native Ubuntu commands only
MCQ checklist:
correct_optionmust match one of theoptions[].idvalues- Always include an
explanation
In-Memory Cache & Hot Reloading
To ensure high performance and zero disk-I/O bottlenecking, scenarios and bundles are cached in memory on backend startup. When developing or updating scenarios, you can hot-reload the definitions without rebuilding the image or restarting the container:
- Mount Scenarios Directory: Run the container with the local
scenarios/directory mounted to/app/scenarios:docker run --rm -itd --privileged -p 7554:80 --name kubekosh -v <path_to_scenarios_directory>:/app/scenarios zeborg/kubekosh:latest
- Reload Cache: Click the Reload Scenario Cache (↻) button in the top right corner of the header in the web user interface, or send an API request:
curl -X POST http://localhost:7554/api/cache/reload
NOTE: The content in
<path_to_scenarios_directory>should be the path to the localscenarios/directory of the cloned repository with your updates, i.e., it should contain the updatedscenarios/dataandscenarios/bundlesdirectories.
Workflow
# 1. Fork the repo on GitHub, then clone your fork git clone https://github.com/<your-username>/kubekosh.git cd kubekosh # 2. Create a branch git checkout -b feat/my-scenario # 3. Add a new scenario file (copy an existing scenario as a template or create a new one) cp scenarios/data/deploy-nginx.json scenarios/data/my-new-scenario.json vim scenarios/data/my-new-scenario.json # edit the new scenario as per [SCHEMA.md](scenarios/SCHEMA.md) # 4. Add the scenario ID to the relevant bundle vim scenarios/bundles/k8s-basics.json # edit the bundle to include the new scenario ID # 5. Build and test locally # Run the built container directly: docker build -t kubekosh . && docker run --rm -itd --privileged -p 7554:80 --name kubekosh kubekosh # Or mount the scenarios folder for hot-reloading: docker run --rm -itd --privileged -p 7554:80 -v $PWD/scenarios:/app/scenarios --name kubekosh zeborg/kubekosh:dev # 6. Commit and push to your fork (example for adding `my-new-scenario` to `k8s-basics` bundle) git add scenarios/data/my-new-scenario.json scenarios/bundles/k8s-basics.json git commit -m "feat: add my-new-scenario to k8s-basics bundle" git push -u origin feat/my-new-scenario
Open a Pull Request from your fork's branch against main.
License
Apache 2.0 License — see LICENSE.































