Free learning / 45–60 minute container lab
Understand what survives a container replacement
An image is the package; a container is one instance. Follow a pending review through a visual lifecycle, then run a real local API to compare restart, replacement and a mounted volume.
No account needed. Use your own copy for writing and keep the original as a reference.
Engineering lab library / Run locally
See what survives replacement.
Predict the lifecycle, then run the API and compare its actual responses. Keep the Python file and Dockerfile together in a new empty folder.
A real local Docker experiment with synthetic data. Preparation downloads an image; no model, cloud account or public service is used.
Start here / Docker · Actual local containers
Container lifecycle · local API
Observe a pending review disappear without a volume and survive replacement when its storage is explicitly retained.
Local Docker Engine or Desktop · curl · Internet for image preparation
A container is one isolated instance of an application. Its image is the reusable package used to create that instance. Docker is a tool for building images and managing containers; neither is a programming language. Start here before choosing a managed container service or putting an agent worker on a server.
You will run a tiny HTTP API, save a fictional pending review, restart its process, then replace its container. First you will lose the record. Then you will keep it by mounting a named volume. No model, GPU or cloud account is involved.
Allow 45–60 minutes. Use Docker Desktop or a local Docker Engine running Linux containers, a terminal and curl. Image preparation needs internet access and local disk space. After the image is prepared, the experiment runs locally. Optional application checks use Python 3.11+ on your computer.
1. Meet the package, process and storage
UNDERSTAND THE SYSTEM
Follow a review through restart and replacement
An image is a reusable application package. A container is one isolated instance made from it, with its own process and writable layer. Follow a fictional review through restart and replacement on one Docker host; then run the free lab to collect your own observations.
See what moves and why.
Read the connections in plain English
- 01Code + Dockerfile → Reusable image
Bundle the program and runtime into one image.
- 02Reusable image → Container instance
Use that image to create a separate container and start its process.
- 03Container instance → Writable layer
The pending review goes into the instance's writable layer.
- 04Container instance → Named volume
The volume supplies the application's storage path instead of the writable layer.
Check your understanding: A review survived docker restart. Does that prove it will survive docker rm followed by docker run?
No. Restart retains that container's writable layer; removal deletes it. Put the record in explicitly retained storage and verify that the replacement actually reads it. A local volume still needs a separate backup and host-recovery plan.
If you know a class and an object, you already know a useful part of the idea: one reusable definition can produce several separate instances. An image is more concrete than a class: it contains filesystem layers and startup configuration. Each container has its own process environment and a writable layer on top of those image layers.
An image does not contain a running server. A container can be running or stopped. A stopped container still exists; removing it is a separate operation.
| Part | In this exercise | What to remember |
|---|---|---|
| Image | Python, the API file and its start command | A reusable package; creating a new instance does not copy another instance's later writes. |
| Container | The process serving port 8080 | One instance, with its own lifecycle and writable layer. |
| Process memory | A freshly generated instance ID | Lost when the process ends, even when the same container restarts. |
| Writable layer | The first /data/review.sqlite3 | Survives stopping and starting that container; disappears when it is removed. |
| Named volume | lockedin-container-101-data mounted at /data | A separate Docker-managed store, reusable by a replacement container. |
A virtual machine supplies its own guest operating system and kernel. Linux containers use the host's kernel; Docker Desktop runs them inside a Linux VM on macOS and Windows. A container image must still match a supported operating system and CPU architecture. Packaging an application does not remove those constraints.
Use the visual walkthrough to predict what changes in each step. The diagram contains authored examples. The commands below produce your actual local evidence.
2. Build the small API image
Create a new, empty folder named lockedin-container-101 and open a terminal there. Download both files into it:
Read both files. The API accepts only the fictional record R-104, with status pending. It has no login system and is only for a local exercise. Do not add customer data or publish it as a service.
Create a file called .dockerignore containing exactly:
*
!fde-container-lab.pyThe build context is the folder sent to the builder. This allowlist keeps unrelated files out of that context. Never use your home folder or a folder containing credentials as the build context.
docker version
docker context show
docker build -f fde-container-lab.Dockerfile -t lockedin-container-lab:1 .
docker image inspect lockedin-container-lab:1 --format '{{.Id}} {{.Config.User}}'Continue only with your local Docker environment. If Docker is configured for a remote host, change to your approved local environment before running this lab. The first build retrieves the official Python base image pinned in the Dockerfile. A digest identifies fixed image content; the lockedin-container-lab:1 tag is the convenient name you give your new local image. Record the resulting image ID.
The final image runs as numeric user 10001:10001. Only /data needs to be writable by that user. EXPOSE 8080 describes the application port; it does not publish a host port by itself.
If Python is installed on your computer, you can also run python3 fde-container-lab.py --check. These check the application storage behavior, not Docker isolation or durability.
3. Start an instance and save a review
Check docker ps -a --filter name=lockedin-container-101 and docker volume ls --filter name=lockedin-container-101-data first. If either name already belongs to work you need, choose different names throughout this exercise. Do not remove an existing resource to make the example work. If host port 8765 is in use, choose another free port and replace 8765 in every command and URL; leave the other service running.
docker run --detach --name lockedin-container-101 \
--publish 127.0.0.1:8765:8080 \
--memory 128m --cpus 0.5 --pids-limit 32 \
--cap-drop ALL --security-opt no-new-privileges \
lockedin-container-lab:1
curl --fail http://127.0.0.1:8765/healthz
curl --fail http://127.0.0.1:8765/review
curl --fail --header 'Content-Type: application/json' \
--data '{"id":"R-104","status":"pending"}' \
http://127.0.0.1:8765/reviewAfter each start or restart, let the server start before making the next request. If a request arrives too early, retry that request after the container is running. A persistent connection failure needs docker logs lockedin-container-101, not a bigger instance.
The first review response has review: null; the POST response contains R-104. The instance field identifies the current process, not a user or a model. Save its actual value.
Read the port mapping from left to right: 127.0.0.1 restricts the published listener to this computer; 8765 is the host port; 8080 is the container's port. Inside its network namespace the server listens on 0.0.0.0 so the forwarded request can reach it. These two addresses serve different purposes.
The resource limits bound this lab's process; they do not make the API an authenticated production service. /healthz only proves this process can answer. It does not prove that a pending review was retained.
4. Restart, then replace without a volume
Predict both the instance ID and the saved review before running each command.
docker restart --timeout 1 lockedin-container-101
curl --fail http://127.0.0.1:8765/review
docker inspect lockedin-container-101 --format '{{.Id}} {{.Image}}'The process instance ID changes. The record remains. Restarting the same container kept its writable layer. A passing restart test alone has not proved that a new release can replace this container safely.
Now stop and remove only the container you created. This deliberately destroys its lab record:
docker stop --timeout 1 lockedin-container-101
docker rm lockedin-container-101
docker run --detach --name lockedin-container-101 \
--publish 127.0.0.1:8765:8080 \
--memory 128m --cpus 0.5 --pids-limit 32 \
--cap-drop ALL --security-opt no-new-privileges \
lockedin-container-lab:1
curl --fail http://127.0.0.1:8765/review
docker inspect lockedin-container-101 --format '{{.Id}} {{.Image}}'The container ID is new; the image ID is the same. The review is now null. Reusing a name did not reuse the removed container's writable layer. The image never contained your later POST.
5. Replace the process and keep the review
Stop and remove that empty lab container. Create a new, explicitly named volume and mount it at the path the application uses:
docker stop --timeout 1 lockedin-container-101
docker rm lockedin-container-101
docker volume create lockedin-container-101-data
docker run --detach --name lockedin-container-101 \
--publish 127.0.0.1:8765:8080 \
--memory 128m --cpus 0.5 --pids-limit 32 \
--cap-drop ALL --security-opt no-new-privileges --read-only \
--mount type=volume,source=lockedin-container-101-data,target=/data \
lockedin-container-lab:1
curl --fail --header 'Content-Type: application/json' \
--data '{"id":"R-104","status":"pending"}' \
http://127.0.0.1:8765/review
docker inspect lockedin-container-101 --format '{{json .Mounts}}'The explicit mount supplies the writable /data directory; the rest of the container filesystem is now read-only. Docker initializes this new empty volume from the image's /data directory, including its ownership. An existing volume may have different permissions; do not fix that by running your application as root.
Remove only this lab container, keeping the volume:
docker stop --timeout 1 lockedin-container-101
docker rm lockedin-container-101Run the same mounted docker run command above again, then request /review. The process and container IDs change; R-104 remains. Record the volume name and mount destination as well as the response. Naming a volume is not enough: the replacement must mount it at the path the application actually reads.
A named volume on one Docker host is not a backup, a cross-region replica or a highly available database. Losing that host or deleting its volume can still lose the review. This exercise proves replacement behavior on one local host only.
6. Carry the decision into a cloud deployment
| Hosting choice | What runs your image | Where the pending review should live |
|---|---|---|
| VM running Docker | You manage the VM, container runtime and service | Explicit durable storage, with a tested backup and recovery plan. |
| Managed container service | The provider manages more of the runtime | A suitable external database or a documented supported persistent mount. |
| Kubernetes | A cluster schedules container workloads into pods | A state service or deliberately configured persistent storage; a pod's replacement is expected. |
Cloud Run, Amazon ECS and Azure Container Apps are examples of container hosting services with different operating models. Do not assume their filesystem behavior matches this local Docker host. Consult the chosen service's storage and lifecycle contract before carrying the design over.
Explain this to a receiving engineer: "The model or worker may be replaceable, but a human's pending review must still exist after deployment." Keep these observations:
- One image ID and the container IDs before and after replacement.
- The actual response after restart, replacement without a mount and replacement with a mount.
- A diagram placing code, process memory, durable reviews and runtime credentials in different locations.
- One remaining failure case, such as losing the storage host, and the recovery test you would need.
Change the design on paper: two workers must read the same review from different hosts. Explain why a local named volume alone no longer satisfies the requirement. Choose a storage service and state the concurrency, access and recovery behavior you would need to verify.
7. Clean up only this experiment
Keep your observation notes, then stop and remove the lab container. The following volume removal deliberately deletes the synthetic record:
docker stop --timeout 1 lockedin-container-101
docker rm lockedin-container-101
docker volume rm lockedin-container-101-data
docker image rm lockedin-container-lab:1
docker ps -a --filter name=lockedin-container-101
docker volume ls --filter name=lockedin-container-101-dataUse the names you actually chose. If a resource is already absent, inspect rather than widening cleanup. Never use a global prune command for this lab. The downloaded base image can remain in Docker's cache for reuse.
8. Read the source explanations
Original LockedIn Labs exercise. Technical references checked 21 October 2026:
- Docker: what is an image?
- Docker: container storage and writable layers
- Docker: named volumes and their lifecycle
- Docker: running containers, port mappings and limits
- Google Cloud Run: container filesystem contract
- AWS: Amazon ECS storage options
- Azure Container Apps: storage mounts
Continue in your authorized Academy lesson to connect replacement behavior to durable jobs, bounded retries, review authority and deployment handoff. Completing this exercise is practice evidence, not a vendor certification or a production-readiness approval.