Practice Ground
Stop reading and start doing. Fix a broken config, read an incident and find the cause, work through an outage in a safe terminal, or build a small project and have it checked.
Browse every exercise below. One is open to read without an account. Sign in free to open them all and check your answers.
18 topics, 132 exercises. Open a topic to see its exercises.
Linux4 exercises
Lock down this sshd_configFix the configintermediateRoot can log in with a password, and anyone with an account can SSH in.Sign in"No space left on device", but df shows free spaceRead the logsintermediateWrites fail on a volume that is only half full. What is really full?Sign inOn-call: the service won't start after a deployTerminal incidentintermediateA service fails to start on a simulated Linux server. Find what is in the way.Sign inOn-call: the disk is fullTerminal incidentintermediateA server stops working because / is 100% full. Find what is filling it and free the space safely.Sign in
Networking3 exercises
Fix the firewall that exposes the databaseFix the configintermediateEverything is allowed by default, SSH is open to the world, and Postgres is reachable from the internet.Sign inIs it DNS, the app, or a firewall?Read the logsintermediateAn API times out. Use dig, nc and curl output to find which layer is broken.Sign inOn-call: the app can't reach the databaseTerminal incidentadvancedPostgreSQL is running, but the app server gets "connection refused". Fix it without exposing the database.Sign in
Git & GitHub9 exercises
Write a .gitignore that keeps secrets outFix the configbeginnerA repo keeps committing .env files, node_modules and build output.Sign inFix this CODEOWNERS file: the catch-all wins over everythingFix the configintermediateThe specific rules never apply, one owner has the wrong syntax, and workflows aren't protected.Sign inMake this pre-commit config trustworthy and fastFix the configintermediateFloating hook versions, no secret scanning, and the whole test suite runs on every commit.Sign inWhy was my push rejected?Read the logsbeginnergit push fails with "fetch first". What happened, and what is the safe fix?Sign infatal: refusing to merge unrelated historiesRead the logsbeginnerA brand-new local project meets a GitHub repository that already has a README.Sign inGH001: the file was deleted, but the push is still rejectedRead the logsintermediateA 150 MB file was committed by mistake. Removing it in a new commit doesn't help.Sign inRecover commits that vanishedTerminal incidentintermediateYou committed on a detached HEAD, switched branch, and your work disappeared. Get it back.Sign inRebase conflict: keep MY change, then push safelyTerminal incidentintermediateRebasing your branch onto main conflicts in a config file. In a rebase, 'ours' and 'theirs' are swapped.Sign inUndo a merged pull request on main, without rewriting historyTerminal incidentintermediateA merged PR broke production. Everyone has already pulled it, so history must not change.Sign in
Shell Scripting3 exercises
Make this deploy script safeFix the configintermediateAn unquoted variable in an rm -rf can delete far more than the cache.Sign inFix the cron job that only works by handFix the configintermediateThe report runs fine in a terminal but never produces output from cron, and runs pile up.Sign inThe backup "succeeded" but the file is emptyRead the logsintermediateA backup job exits 0 every night, yet the restore test found a 20-byte file.Sign in
Python3 exercises
Make this health-check script trustworthyFix the configbeginnerIt can hang forever, swallows every error, leaks a token, and always exits 0.Sign inIt works on my laptop: reading a Python tracebackRead the logsbeginnerA deploy script crashes in CI with a KeyError. Read the traceback from the bottom up.Sign inGet a Python script running on a fresh serverTerminal incidentintermediateIt worked on the laptop. On Ubuntu 24.04, pip refuses to install anything. Use a virtual environment.Sign in
Docker15 exercises
Fix the slow, root-running DockerfileFix the configbeginnerFree previewEvery code change reinstalls all dependencies, and the container runs as root.Fix the unpinned, bloated DockerfileFix the configbeginnerThe image is 1.9GB, ships debugging tools, and docker stop is slow because of it.Sign inTurn this 1 GB Go image into a small, safe oneFix the configintermediateThe final image ships the whole Go toolchain and source code, and runs as root.Sign inFix the Compose file: API crashes at startup and the database is exposedFix the configintermediateThe API starts before Postgres is ready, the database port is published, and the data has no named volume.Sign inStop leaking the npm token into the imageFix the configadvancedA private-registry token passed as a build argument is recorded in the image's history.Sign inHarden this Compose serviceFix the configintermediateA background worker runs privileged, as root, with no limits, unbounded logs and a writable filesystem.Sign inA Docker build fails: no space left on deviceRead the logsintermediateThe build server's disk filled up. Find what is using it and clean up safely.Sign inA container is stuck "unhealthy" even though it worksRead the logsintermediateThe API answers fine when tested directly, but Docker keeps marking it unhealthy.Sign in"Permission denied" on a bind-mounted folderRead the logsintermediateThe container can't write to a host directory it was given. It worked on the developer's laptop.Sign inThe API can't find the database by nameRead the logsintermediateTwo containers on the same host, but one fails with ENOTFOUND for the other's name.Sign in"exec format error": the image works on my laptopRead the logsbeginnerAn image built on a new laptop refuses to run on the production server.Sign inThe container won't start: port is already allocatedTerminal incidentintermediateAnother team's container holds port 8080. Start yours without breaking theirs.Sign inThe database looks empty after a container was recreatedTerminal incidentadvancedA container was replaced without a named volume. The old data may still be on disk.Sign indocker compose: the API is unhealthy, but the database is fineTerminal incidentintermediateA wrong hostname in a .env file, and the trap that restart doesn't re-read it.Sign inThe port is published, but the app doesn't answerTerminal incidentintermediatedocker ps shows 8080 mapped, yet curl from the host gets an empty reply.Sign in
Kubernetes15 exercises
Make this Deployment safe to roll outFix the configintermediateNo probes, no resource settings and a floating image tag.Sign inFix the Ingress that never routes trafficFix the configintermediateThe controller ignores it, the backend names a Service that doesn't exist, and there is no HTTPS.Sign inMake this CronJob safe to leave runningFix the configintermediateIt runs every minute for a 3-minute job, retries forever, keeps hundreds of old Jobs, and has an invalid restart policy.Sign inCut this RBAC down to read-only accessFix the configintermediateA service account that only needs to look at Pods has been bound to cluster-admin.Sign inLock the database down with a NetworkPolicyFix the configadvancedThe policy selects every Pod and allows all traffic in. Only the API should reach Postgres.Sign inWhy does the autoscaler show <unknown> and never scale?Fix the configintermediateThe HPA targets CPU utilisation, but the Deployment declares no CPU request to measure against.Sign inOrders API returns 503 after a deployRead the logsintermediateConnection pool timeouts started right after a release. What changed?Sign inWhy does this Pod keep restarting?Read the logsbeginnerThe restart count keeps climbing. Read the Pod's last state.Sign inImagePullBackOff from a private registryRead the logsintermediateThe image exists and the tag is right, but the node is refused when it tries to pull it.Sign inA Pod stuck in Pending: 0/3 nodes are availableRead the logsintermediateNothing is crashing. The Pod simply never gets a home. Three nodes, and none of them fits.Sign inA rolling update that stops halfwayRead the logsadvancedThe new version rolled out to one Pod and stalled. Users still get answers. Why, and what should you do?Sign inOn-call: the orders service is crash-loopingTerminal incidentintermediateInvestigate a crash-looping Pod in a simulated cluster, fix it, and confirm the fix.Sign inCreateContainerConfigError: the ConfigMap exists, just not hereTerminal incidentintermediateThe Pod can't start because a ConfigMap it references is missing from its namespace.Sign inThe Service has no endpointsTerminal incidentintermediateAll three Pods are healthy, yet nothing reaches them through the Service.Sign inkubectl drain hangs: cannot evict, disruption budgetTerminal incidentadvancedYou need to patch node-2. Draining it is refused because it would break the API's availability guarantee.Sign in
Helm9 exercises
Make these Helm values safe for productionFix the configintermediateOne replica, a floating tag that hides a YAML number trap, no limits, and a password in the file.Sign inMake this Helm template survive missing and non-string valuesFix the configintermediateAn empty image tag is a YAML error, and booleans and numbers reach Kubernetes as the wrong type.Sign inFix this Chart.yaml: floats, an old apiVersion and loose dependenciesFix the configintermediatehelm lint rejects it, and even when it is fixed the dependency versions could change under you.Sign inTurn this database migration Job into a Helm hookFix the configadvancedIt is an ordinary resource, so the second upgrade fails on an immutable field, and it can never run before the new pods.Sign inhelm upgrade: "another operation is in progress"Read the logsadvancedA release is stuck after a cancelled CI job. Read the history and unstick it safely.Sign inhelm lint: "mapping values are not allowed in this context"Read the logsintermediateA chart that looks fine in the template fails to render. The rendered output shows why.Sign inhelm upgrade: "spec.selector: ... field is immutable"Read the logsadvancedThe upgrade worked for months. After a labelling clean-up in the chart it fails, and the release is still running.Sign inHelm says 'deployed', the pods are crash-looping: roll backTerminal incidentintermediateRevision 7 was released this morning. Helm reports success, users report errors.Sign inI only changed the replica count, and the image went backTerminal incidentadvancedA scaling upgrade with one --set silently reset the image tag and the log level to the chart defaults.Sign in
CI/CD10 exercises
Make this rollout zero-downtime, and easy to roll backFix the configintermediateHalf the pods are removed before any new pod exists, a stuck rollout hangs for ever, and history is unlimited.Sign inBuild once, deploy the same artifact everywhereFix the configintermediateProduction is rebuilt from source under the mutable tag 'latest', and deploys automatically on every push.Sign inReplace long-lived cloud keys in CI with short-lived credentialsFix the configadvancedKeys are committed in the pipeline file, and the job prints its secrets to a log everyone can read.Sign inThe pipeline broke, but nobody changed the codeRead the logsbeginnerBuild and tests pass, then the image push is denied. Read the run and find the real cause.Sign inThe pipeline fails one run in five, and passes when re-runRead the logsintermediateDevelopers now click 'retry' before they read the failure. What is really wrong?Sign inThe rollback made things worse: the old version can't startRead the logsadvancedThe new release broke checkout, so it was rolled back. Now nothing works at all.Sign inWhere does the time go? Reading four delivery metricsRead the logsintermediateThe team wants to buy faster CI runners. The numbers say something different.Sign inBlue/green release: switch traffic, watch, and roll back in secondsTerminal incidentintermediateGreen passes its checks, but the moment real traffic arrives the error rate jumps. Go back without redeploying.Sign inPromote the tested image to production without rebuilding itTerminal incidentadvancedStaging tested exactly one image. Get those exact bytes into the production registry and deploy them by digest.Sign inThe nightly build broke somewhere in the last eight commitsTerminal incidentintermediateFind the exact commit that introduced a failure by letting git bisect run your smoke test.Sign in
Jenkins9 exercises
Secure this JenkinsfileFix the configintermediateA deploy token is committed in the pipeline, and nothing stops a hung build.Sign inMake this Jenkinsfile safe to run on every branchFix the configintermediateIt deploys to production from any branch without asking, keeps every build forever and leaves workspaces behind.Sign inStop Groovy interpolating a secret and a parameter into the shellFix the configadvancedThe token ends up in the build log, and a build parameter can inject shell commands.Sign inThe Jenkins build fails at checkoutRead the logsbeginnerThe build stopped before compiling anything. Find out why.Sign inBuilds queue forever: the only Docker agent went offlineRead the logsintermediateJobs sit at 'Waiting for next available executor', and the node has been switched off by Jenkins itself.Sign inRejectedAccessException: Scripts not permitted to use methodRead the logsadvancedA Jenkinsfile that lists some files fails in the Groovy sandbox, and an approval request is waiting.Sign inOn-call: Jenkins is crawling and builds are stuckTerminal incidentadvancedThe controller keeps running out of Java heap. Size it properly and apply it the way systemd expects.Sign inJenkins won't start after a plugin update: roll the plugin backTerminal incidentadvancedFriday's update installed a Git plugin that needs a newer Jenkins. The UI returns 503.Sign inTriggering a Jenkins build from a script: 403 No valid crumbTerminal incidentadvancedA deploy script that used the account password now gets 403. Use an API token, and follow the build to its result.Sign in
GitHub Actions10 exercises
Harden this GitHub Actions workflowFix the configadvancedA script-injection hole, a wide-open token and a moving third-party action.Sign inMake this CI workflow fast, and stop it running twiceFix the configintermediateThe cache never hits, every push to a pull request runs twice, and one failing Node version hides the others.Sign inTurn this workflow into a proper reusable workflowFix the configintermediateIt reads inputs and secrets that nothing declares, and it still runs on every push by itself.Sign inReplace stored AWS keys with OIDC in this deploy workflowFix the configadvancedThe workflow uses long-lived keys from repository secrets. OIDC gives it short-lived credentials and nothing to steal.Sign inThe workflow never ran, and the pull request is stuck waitingRead the logsintermediateA push started nothing, and the required check on the pull request says 'Waiting for status to be reported'.Sign in"Resource not accessible by integration" when commenting on a pull requestRead the logsintermediateA preview workflow deploys fine, and then fails at the step that posts the link as a comment.Sign inA red check on my pull request: find out why, and re-run only what failedTerminal incidentintermediateRead the failing run with gh, decide whether it is a real failure or a flaky one, and re-run just the failed job.Sign inDeploy version 1.7.3 to staging by dispatching the workflowTerminal incidentintermediateFind out what inputs the Deploy workflow takes, start it from the terminal, and follow the run. Production is not the target.Sign inRotate a leaked deploy token everywhere it is stored as an Actions secretTerminal incidentadvancedThe same token lives as a repository secret and as a production environment secret. Replace both without printing it.Sign inBuild a CI pipeline with GitHub ActionsGuided projectbeginnerAdd a workflow to your own repository that tests every pull request, then have it verified.Sign in
Terraform15 exercises
Remove the password from this TerraformFix the configintermediateA database password is committed in the config, and the database is publicly reachable.Sign inLock down this public S3 bucketFix the configintermediateThe bucket is public-read, unversioned and unencrypted. With the v4+ AWS provider each setting is its own resource.Sign inPin the versions and move the state off a laptopFix the configintermediateNo version constraints, and the state is a local file, so two engineers can overwrite each other.Sign inStop removing one subnet from destroying the restFix the configadvancedSubnets are created with count over a list, so deleting an item shifts every index after it.Sign inTurn this hardcoded resource into a reusable moduleFix the configintermediateEverything is a literal, there are no inputs, and nothing is exported for the caller.Sign inStop CI applying Terraform on every pushFix the configadvancedEvery push to any branch runs apply with -auto-approve, with no plan review and no approval.Sign inTerraform says the state is lockedRead the logsintermediateA pipeline run fails on the state lock. Decide what is safe to do.Sign inError: Cycle between two security groupsRead the logsintermediateTwo security groups each allow traffic from the other, and plan refuses to start.Sign ininit fails: the locked provider doesn't match the constraintRead the logsintermediateSomeone bumped the AWS provider constraint in code and now init refuses to run.Sign inPlan is never clean: the same change, every timeRead the logsintermediateNothing was edited, yet every plan wants to update the same instance.Sign inAn apply that failed halfwayRead the logsadvancedThe VPC and subnet were created, then the instance hit a quota. What state is everything in now?Sign inTerraform wants to destroy the production databaseTerminal incidentadvancedA rename in the code makes terraform plan replace the database. Fix the state, not the database.Sign inThe bucket already exists: bring it under TerraformTerminal incidentintermediateSomeone created the bucket by hand. Now Terraform tries to create it and fails.Sign inForce Terraform to recreate one broken instanceTerminal incidentintermediateThe instance is wedged, but Terraform says there are no changes. Replace just that one resource.Sign interraform plan fails: the security token is expiredTerminal incidentadvancedYou logged in with SSO this morning, yet Terraform still can't authenticate. A stale variable is winning.Sign in
Ansible15 exercises
Make this playbook idempotentFix the configintermediateRun twice, it fails on the second run and keeps appending a line to the config.Sign inGet the database password out of this playbookFix the configintermediateThe password is a plain-text variable, and both tasks that use it will print it in logs and diffs.Sign inStop restarting nginx on every run, and never with a broken configFix the configintermediateA plain restart task runs every time, and a config typo takes nginx down.Sign inOne package task per package, and it fails on Rocky LinuxFix the configintermediateThree copies of the same task, all hard-wired to apt, run against a fleet that is half Rocky Linux.Sign inTidy this inventory: no repetition, no passwordsFix the configintermediateEvery host repeats the same variables, the SSH password sits in plain text, and nothing is grouped.Sign inMake this Ansible deploy workflow safeFix the configadvancedIt runs against production on every push, with host key checking off and secrets left on disk.Sign inUNREACHABLE and FAILED: what is the difference?Read the logsbeginnerOne host can't be logged into, another logs in but can't use sudo. Read the recap.Sign in'db_host' is undefined, but only on one serverRead the logsintermediateThe same playbook works on one host and fails on the other. Same role, same template.Sign inThe config is fixed on disk, but nginx still serves the old oneRead the logsadvancedThe first run failed midway, the second run fixed it, and the handler never ran.Sign inSyntax Error while loading YAML: found unacceptable keyRead the logsbeginnerA template variable at the start of a value breaks the whole playbook before anything runs.Sign inUNREACHABLE: REMOTE HOST IDENTIFICATION HAS CHANGEDRead the logsintermediateA server that was reachable yesterday is refused today. The tempting fix is dangerous.Sign inRoll out a config change safely with AnsibleTerminal incidentadvancedPush a changed nginx template to six web servers without taking them all down at once.Sign inSecrets were committed in plain text: encrypt them with VaultTerminal incidentintermediategroup_vars/prod/vault.yml holds a real password and API token in clear text.Sign inTwo of twelve servers failed: fix them and re-run only thoseTerminal incidentintermediateThe play succeeded on ten hosts and failed on two. Don't touch the ten that worked.Sign inThe task is 'skipped' on every server it should run onTerminal incidentintermediateA `when:` condition never matches. Find out what the fact really says.Sign in
Monitoring3 exercises
Fix the CPU alert that pages for the wrong reasonsFix the configintermediateIt flaps on every spike, averages away a single hot server, and tells the on-call nothing.Sign in"Disk will fill in 4 hours": what do you do?Read the logsintermediateA predictive alert fires before the disk is full. Work out how long you have, and what to do first.Sign inOn-call: the 5xx alert is firingTerminal incidentintermediate12% of requests are failing. Use the access log to find where the errors come from, then why.Sign in
AWS3 exercises
Cut this IAM policy down to least privilegeFix the configintermediateA reporting service can do anything to every S3 bucket, but it only needs to read one.Sign in"AccessDenied" reading S3 from an EC2 instanceRead the logsintermediateThe same command works from a laptop and fails on the server. Find out who is really calling.Sign inOn-call: the ECS service has zero running tasksTerminal incidentintermediateA deploy left the service at desired 2, running 0. Find why and get back to a working version.Sign in
Azure2 exercises
Close the SSH and RDP ports to the internetFix the configbeginnerA network security group lets anyone on the internet try to log in to the web VMs.Sign inA new Container Apps revision won't startRead the logsintermediateA deployment created a new revision that never becomes healthy. Is production down?Sign in
GCP2 exercises
Fix the over-privileged Cloud Run deployFix the configintermediateA broad Editor role, services running as the default identity, and a public background worker.Sign inA 403 from Cloud Run: what is missing?Read the logsintermediateA private Cloud Run service returns 403 Forbidden. Separate "who may call it" from "what it can do".Sign in
Windows2 exercises
Make this cleanup script impossible to misuseFix the configintermediateRun without a path from Task Scheduler, it deleted files from the wrong folder. Add the guard rails.Sign inThe Windows service won't start after the weekendRead the logsintermediateEvent Viewer shows two errors. Which is the cause, which is the symptom, and what stops it happening again?Sign in