How It Works
Understand the technical architecture behind Kubeasy — from the local cluster to validation.
Kubeasy combines several open-source tools to create a fully local, isolated, and validated Kubernetes learning environment. This page explains how each piece fits together.
Architecture overview
┌────────────────────────────────────────────────────────────┐
│ Your Machine │
│ │
│ ┌──────────────┐ │
│ │ Kubeasy CLI │ (Go + Cobra) │
│ │ - Manages │ │
│ │ - Validates │ │
│ └──────┬───────┘ │
│ │ │
│ │ Creates & Queries │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ Kind Cluster (Local Kubernetes) ││
│ │ ││
│ │ ┌────────────┐ ┌──────────────┐ ┌──────────────────┐ ││
│ │ │ Kyverno │ │ cert-manager │ │ nginx-ingress │ ││
│ │ └────────────┘ └──────────────┘ └──────────────────┘ ││
│ │ ┌────────────────────────────────────────────────────┐ ││
│ │ │ Challenge Namespaces (isolated) │ ││
│ │ │ • pod-evicted │ ││
│ │ │ • rbac-missing-permissions │ ││
│ │ │ • ... │ ││
│ │ └────────────────────────────────────────────────────┘ ││
│ └─────────────────────────────────────────────────────────┘│
└────────────────────────────────────────────────────────────┘The Kubeasy CLI
The CLI is the central tool. It handles everything: authentication, cluster setup, challenge deployment, validation, and progress submission.
Built with Go and Cobra, it ships as a single binary with no runtime dependencies.
Core responsibilities:
- Authenticate users via API keys
- Create and configure the local Kind cluster
- Install and verify infrastructure components
- Deploy challenges from OCI artifacts
- Execute validation logic directly against the cluster
- Submit results to the Kubeasy platform
Kind — local Kubernetes
Kind (Kubernetes in Docker) runs a real Kubernetes cluster inside Docker containers. When you run kubeasy setup, the CLI:
- Creates a single-node cluster named
kubeasyrunning Kubernetes v1.35.0 - Configures port mappings: host
8080→ cluster80(HTTP) and host8443→ cluster443(HTTPS), used by the nginx-ingress controller - Configures
kubectlto point to the cluster
Kind clusters are fast to create, isolated from your system, and behave like real Kubernetes. You can use any standard Kubernetes tooling against them.
Infrastructure components
kubeasy setup installs 7 infrastructure components into the cluster. These support the challenge ecosystem:
| Component | Version | Namespace | Purpose |
|---|---|---|---|
| Kyverno | v1.17.1 | kyverno | Policy engine for bypass prevention |
| local-path-provisioner | v0.0.35 | local-path-storage | PersistentVolume storage |
| nginx-ingress | v1.15.0 | ingress-nginx | Ingress controller |
| Gateway API CRDs | v1.5.1 | — | Gateway API support |
| cert-manager | v1.20.0 | cert-manager | TLS certificate management |
| kubeasy-ca | — | cert-manager | Self-signed CA + ClusterIssuer |
| cloud-provider-kind | v0.10.0 | — | LoadBalancer support |
You can inspect any component at any time:
kubectl get pods -n kyverno
kubectl get pods -n ingress-nginx
kubectl get pods -n cert-managerKyverno — bypass prevention
Kyverno is a Kubernetes-native policy engine. In Kubeasy, it's used exclusively to prevent cheating — it enforces the rules defined in each challenge's policies/ directory.
For example, a policy might prevent you from swapping the broken application image with a working one. This ensures you solve the actual problem rather than working around it.
# Example: prevents changing the container image
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: protect-app-image
namespace: pod-evicted # matches the challenge slug
spec:
validationFailureAction: Enforce
rules:
- name: preserve-image
match:
resources:
kinds: ["Deployment"]
validate:
message: "Cannot change the container image"
pattern:
spec:
template:
spec:
containers:
- image: "myapp:broken-v1"Kyverno is only used for bypass prevention. Challenge validation is handled entirely by the CLI — not Kyverno.
Challenge deployment via OCI
Challenges are packaged as OCI artifacts and stored in the GitHub Container Registry (ghcr.io/kubeasy-dev/challenges). When you run kubeasy challenge start <slug>, the CLI:
- Pulls the OCI artifact for that challenge
- Creates a dedicated namespace (
<slug>) - Applies the challenge manifests (the intentionally broken state)
- Applies the Kyverno policies (bypass prevention)
- Waits for resources to reach their initial state
- Switches your
kubectlcontext to the challenge namespace
This approach requires no cluster-side GitOps tooling — the CLI handles everything directly.
CLI-based validation
When you run kubeasy challenge submit <slug>, the CLI:
- Reads the validation objectives from the challenge definition
- Executes each validation in parallel against the cluster
- Groups and displays results
- Sends the structured results to the Kubeasy backend
- The backend verifies all objectives are present and passed before awarding XP
Kubeasy supports 8 validation types:
| Type | What it checks |
|---|---|
condition | Resource conditions (Pod Ready, Deployment Available) |
status | Arbitrary status fields with operators (restart count < 3) |
log | Expected strings in container logs |
event | Forbidden or required Kubernetes events |
connectivity | HTTP connectivity between pods or from the CLI |
rbac | ServiceAccount permissions via SubjectAccessReview |
spec | Resource manifest fields (spec/metadata paths) |
triggered | Run an action then validate the outcome |
Challenge lifecycle
Starting a challenge
kubeasy challenge start pod-evicted
│
├── Fetch challenge metadata from API
├── Create namespace: pod-evicted
├── Pull OCI artifact: ghcr.io/kubeasy-dev/challenges/pod-evicted:latest
├── Apply manifests (broken state)
├── Apply Kyverno policies
└── Set kubectl context to pod-evicted namespaceSolving
You now have full access to the cluster namespace. Use any Kubernetes tooling to investigate and fix the problem. The broken state is realistic — you'll find the same kind of evidence (events, logs, conditions) that you would in a real cluster.
Submitting
kubeasy challenge submit pod-evicted
│
├── Load objectives from challenge definition
├── Execute each validation against the cluster (parallel)
├── Display pass/fail results with messages
└── POST results to Kubeasy API
│
├── Backend verifies ALL expected objectives present
├── Backend verifies no extra objectives injected
└── Awards XP if all objectives passedResetting
# Remove resources and reset backend progress
kubeasy challenge reset pod-evicted
# Remove resources only (keep backend progress)
kubeasy challenge clean pod-evictedSecurity and isolation
Local isolation: Each challenge runs in its own Kubernetes namespace. Challenges don't interfere with each other. The Kind cluster is entirely local — no data leaves your machine.
Bypass prevention: Kyverno policies prevent shortcuts like swapping the broken application with a working one. You must solve the actual problem.
Submission integrity: The backend validates submissions by:
- Checking that ALL registered objectives are present (you can't skip objectives)
- Checking that no unknown objectives were submitted (you can't fabricate results)
- Verifying every objective has
passed: truebefore completing the challenge
Philosophy: why CLI-based validation?
Earlier versions of Kubeasy used Rego-based policies and a Kubernetes operator with CRDs. The current CLI-based approach offers several advantages:
- Simplicity — one
challenge.yamlfile contains everything, including validation logic - No extra infrastructure — the CLI executes all validation directly, no operator needed
- Easy local testing — challenge authors can test validations without pushing to a registry
- Better feedback — the CLI produces detailed, actionable failure messages
- Faster iteration — no reconciliation loop, results are immediate