Creating Your First Challenge
Step-by-step guide to building a complete Kubeasy challenge from scratch.
This guide walks you through creating a complete Kubeasy challenge. By the end, you'll have a working, locally-tested challenge ready to submit.
Prerequisites
- The Kubeasy CLI installed and
kubeasy setupcompleted (you need the Kind cluster) - Basic familiarity with Kubernetes manifests and
kubectl
Step 1: Design your challenge
Before writing any YAML, answer these questions:
- What Kubernetes concept does this teach? (e.g., resource limits, RBAC, probes)
- What challenge type fits?
fix(something is broken),operate(run an operational task),improve(harden a working setup), ormigrate(transform an existing setup) - What is the broken/initial scenario? (e.g., pod gets OOMKilled due to a low memory limit)
- How do you verify it's fixed? (e.g., pod runs stably, no OOM events)
- Is this realistic? Does this problem occur in production?
Example design
We'll create a fix challenge about resource limits:
- Type:
fix - Concept: Kubernetes memory limits and OOMKilled pods
- Scenario: Pod keeps crashing because its memory limit is too low
- Success criteria: Pod runs stably, no OOMKilled events
- Realistic: Yes — one of the most common production issues
Step 2: Scaffold the challenge
Use kubeasy dev create to scaffold the directory structure:
cd path/to/challenges-repo
kubeasy dev createIn interactive mode, you'll be prompted for each field:
Challenge name: Memory Pressure
Slug [memory-pressure]:
Type (fix/operate/improve/migrate): fix
Theme: resources-scaling
Difficulty (easy/medium/hard): easy
Estimated time (minutes) [30]: 15
Created challenge directory: memory-pressure/
memory-pressure/challenge.yaml
memory-pressure/manifests/
memory-pressure/policies/Step 3: Fill in challenge.yaml
Edit memory-pressure/challenge.yaml. The scaffold generates a template with placeholder values — replace them with your content.
Key rules:
- Description: symptoms only, never the root cause
- Objective: the goal, not the method
- Validation titles: generic, not revealing
title: Memory Pressure
description: |
A data processing service keeps restarting unexpectedly.
It worked fine last week — no code changes were made.
theme: resources-scaling
difficulty: easy
type: fix
estimatedTime: 15
initialSituation: |
A data processing pod is deployed.
It starts but crashes within seconds and enters CrashLoopBackOff.
objective: |
Make the application run stably without being killed by Kubernetes.
objectives:
- key: pod-ready
title: "Pod Ready"
description: "The pod must be running and ready"
order: 1
type: condition
spec:
target:
kind: Pod
labelSelector:
app: data-processor
checks:
- type: Ready
status: "True"
- key: stable-operation
title: "Stable Operation"
description: "No crash or eviction events in the past 5 minutes"
order: 2
type: event
spec:
target:
kind: Pod
labelSelector:
app: data-processor
forbiddenReasons:
- "OOMKilled"
- "Evicted"
sinceSeconds: 300Step 4: Create the broken manifests
Create memory-pressure/manifests/deployment.yaml with the intentionally broken state:
apiVersion: apps/v1
kind: Deployment
metadata:
name: data-processor
spec:
replicas: 1
selector:
matchLabels:
app: data-processor
template:
metadata:
labels:
app: data-processor
spec:
containers:
- name: processor
image: python:3.11-slim
command:
- python
- -c
- |
import time
data = []
for i in range(50):
data.append("x" * 1024 * 1024) # ~50MB total
time.sleep(0.1)
print("Done!")
while True:
time.sleep(60)
resources:
limits:
memory: "32Mi"
cpu: "100m"
requests:
memory: "32Mi"
cpu: "50m"The application needs ~50MB of memory, but the limit is 32Mi. Kubernetes will OOMKill the container when it exceeds the limit.
No spoiler comments
Do not add comments that reveal the root cause (# limit too low, # BUG, # TODO: increase this). Learners can read all YAML comments via kubectl get -o yaml. See Challenge Design Guidelines for the full rule.
Custom images
If inline commands aren't suitable for your scenario, add an image/ directory with a Dockerfile. The CLI will build and load it into Kind automatically during local development.
Step 5: Add bypass protection
Create memory-pressure/policies/protect.yaml to prevent users from swapping the broken application for a working one:
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: protect-memory-pressure
namespace: memory-pressure # must match the challenge slug
spec:
validationFailureAction: Enforce
rules:
- name: preserve-image
match:
resources:
kinds: ["Deployment"]
names: ["data-processor"]
validate:
message: "Cannot change the container image"
pattern:
spec:
template:
spec:
containers:
- name: processor
image: "python:3.11-slim"Step 6: Lint
Validate the challenge structure before deploying:
kubeasy dev lint --dir ./memory-pressureLinting challenge: memory-pressure
✓ All checks passed (0 errors, 0 warnings)Fix any reported errors before proceeding.
Step 7: Test the broken state
Deploy the challenge and verify that the problem is reproducible:
kubeasy dev apply --dir ./memory-pressure --cleanCheck that the pod is crashing:
kubectl get pods -n memory-pressure
# NAME READY STATUS RESTARTS AGE
# data-processor-xxx 0/1 CrashLoopBackOff 3 2m
kubectl describe pod -n memory-pressure -l app=data-processor
# Look for OOMKilled in the eventsStep 8: Test the solution
Apply a fix manually and verify your validations pass:
kubectl patch deployment data-processor -n memory-pressure \
--type='json' -p='[
{"op": "replace", "path": "/spec/template/spec/containers/0/resources/limits/memory", "value": "128Mi"},
{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/memory", "value": "64Mi"}
]'Wait for the pod to stabilize, then run validations:
kubeasy dev validate --dir ./memory-pressureCondition Validation
pod-ready: PASSED - All condition checks passed
Event Validation
stable-operation: PASSED - No forbidden events found
All validations passed! (2/2)Step 9: Verify bypass protection
Try the bypass that your Kyverno policy should block:
kubectl set image deployment/data-processor processor=nginx:latest -n memory-pressure
# Expected: admission webhook denied the requestStep 10: Clean up and submit
kubeasy dev clean --dir ./memory-pressureCreate a branch and open a pull request:
git checkout -b challenge/memory-pressure
git add memory-pressure/
git commit -m "feat: add memory-pressure challenge"
git push origin challenge/memory-pressureSee Contributing Guidelines for the PR template and review process.
Common mistakes
Revealing the root cause in descriptions
# BAD
description: The memory limit is too low. Increase it to fix the crash.
# GOOD
description: A data processing service keeps restarting unexpectedly.Validation titles that give away the answer
# BAD
- key: memory-fix
title: "Memory Limit Set to 128Mi"
# GOOD
- key: stable-operation
title: "Stable Operation"No bypass protection
Without Kyverno policies, a user can replace the broken app with a working nginx container and pass all validations without solving the problem.
Checking the implementation, not the outcome
# BAD: only one specific value passes
- field: "containerStatuses[0].restartCount"
operator: "=="
value: 0
# GOOD: any low restart count passes
- field: "containerStatuses[0].restartCount"
operator: "<"
value: 3Checklist
Before submitting a PR:
-
challenge.yamlhas all required fields and correcttype(fix/operate/improve/migrate) - Description and
initialSituationdescribe symptoms only — no field names, error reasons, or durations - Validation titles don't reveal the solution
- Manifests have no spoiler comments (
# BUG,# TODO,# fix this) - Kyverno policy messages don't hint at the fix (no "fix X instead", "add Y", "the problem is Z")
- Kyverno policies prevent image swap, command rewrite, and challenge-specific bypasses
-
kubeasy dev lintpasses - Broken state verified manually (pod crashes, service unreachable, etc.)
- Fix verified — all validations pass after applying the solution
- Key validations use
specchecks to enforce the learning artifact, not just behavioral outcomes - Alternative valid solutions tested where applicable
- Estimated time is realistic
For a full explanation of each rule, see Challenge Design Guidelines.
Next steps
- Validation Overview — learn the philosophy and all 8 validation types
- Testing Challenges — comprehensive testing strategies
- Contributing — PR requirements and review process