Skip to content

Docker Foundations

Docker Foundations is an optional track. Linux gameplay never requires a container engine, and OpsQuest never pulls an image automatically. The track works with Docker Engine, Docker Desktop, or OrbStack through the Docker CLI.

Prepare the first lab

Install and start your chosen Docker-compatible engine, then explicitly fetch the pinned fixture image:

$ docker pull docker.io/library/busybox@sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662
$ opsquest doctor
$ opsquest play 20

Use OrbStack on macOS

OrbStack exposes its engine through the standard Docker CLI and the orbstack Docker context. Select that context for both the image pull and OpsQuest so they use the same image store:

$ DOCKER_CONTEXT=orbstack docker pull docker.io/library/busybox@sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662
$ DOCKER_CONTEXT=orbstack opsquest doctor
$ DOCKER_CONTEXT=orbstack opsquest play 20

These one-command environment settings leave your global Docker context unchanged. Alternatively, run docker context use orbstack once and then use the ordinary commands above. opsquest doctor identifies OrbStack when its official context is active.

You can also discover the track explicitly:

$ opsquest map --track docker
$ opsquest play --track docker

Current lessons

The track has 16 missions in three worlds. World 1: It Works on My Machine is 6 beginner missions covering a deliberately narrow lifecycle loop:

  1. Container Census — list containers and start an existing stopped service.
  2. Last Broadcast — read bounded logs from an exited one-shot job.
  3. Exit Code Detective — distinguish success from failure using sanitized inspection state.
  4. Quiet the Worker — stop one target while preserving a healthy service.
  5. Recovery Pair — restore two stopped services without replacing them.
  6. Shift Handoff — combine start and stop while preserving supporting metrics.

World 2: Container Triage adds health checks, restart policies, log tails, and removal:

  1. Janitor Duty — remove finished job containers while services keep running.
  2. Tail End — show only the last lines of a long job log.
  3. Running Isn't Healthy — pull a running but unhealthy replica from service and start its standby.
  4. Crash Loop — stop a container its restart policy keeps relaunching, then show why it fails.
  5. Postmortem Triage — combine health, exit codes, stop, and remove, and keep the evidence the postmortem needs.

World 3: Network Plumbing teaches which containers can reach each other:

  1. Network Census — read network membership from container and network inspections.
  2. Can't Reach the DB — connect a service to a second network without exposing the database to the web tier.
  3. Need to Know — disconnect a job from a network it should never have joined.
  4. Private Channel — create a network and move two services onto it.
  5. Segmentation — split a flat network into tiers and retire a stale network along with the container holding it open.

Teaching subset

Command Supported forms
List docker ps or docker container ls, with -a/--all and up to four --filter/-f values: status=created\|running\|restarting\|exited or health=starting\|healthy\|unhealthy\|none
Lifecycle start, restart, and stop with one alias
Remove docker rm ALIAS for a stopped container; --force is refused, so stop the container first
Inspect docker inspect ALIAS shows logical state, exit code, health, restart count, and restart policy
Logs docker logs ALIAS or docker logs --tail N ALIAS (also -n N, --tail=N, and --tail all)
Networks docker network ls, inspect NETWORK, create NETWORK, rm NETWORK, connect NETWORK ALIAS, and disconnect NETWORK ALIAS, with no options

Every container command also has a docker container ... form. OpsQuest parses these forms itself and accepts exact logical aliases. It never forwards other Docker CLI arguments or flags, and filters run inside OpsQuest rather than being passed to Docker.

Isolation boundary

OpsQuest generates unique names and ownership labels, maps player-visible aliases to exact container IDs, applies resource restrictions, and removes only resources verified as belonging to the current attempt. Labs do not use privileged mode, host bind mounts, host networking, devices, or a mounted Docker socket.

Every lab network is created by OpsQuest as an internal network, so it has no route to your host network or the internet. Networks carry the same ownership labels as containers, and you can create at most 4 per attempt. docker network rm is refused while any container is attached, even a stopped one, so no container is left unable to start. The built-in bridge, host, none, and default networks are never reachable. Containers without declared networks run with networking disabled and cannot join a network.

Health probes and restart policies are fixed fixture behaviors chosen by the mission, never player input. A crash-loop fixture's restart policy allows at most 50 retries.

Cleaning up after a crash

OpsQuest removes a lab's containers, then its networks, when the attempt ends. If the process is killed first, they stay behind. Their processes exit on their own within 24 hours, and the next Docker mission removes them. You can also check and clean up yourself:

$ opsquest doctor            # reports orphaned lab containers and networks
$ opsquest doctor --cleanup  # removes them

A container or network counts as orphaned only when it has every OpsQuest ownership label and its generated name, and either its recorded owner process on this machine has exited or it is older than 24 hours. Containers belonging to other running OpsQuest sessions on this machine are left alone for their 24-hour lifetime.

The selected Docker-compatible engine remains a powerful external dependency. OpsQuest constrains the lesson and cleanup scope; it does not present the engine itself as an untrusted-code security boundary.

See Sandbox and safety for the full lifecycle and the roadmap for possible later expansions.