# Understand what survives a container replacement

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

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:

- [The teaching API](https://training.lockedinlabs.ai/templates/fde-container-lab.py)
- [Its Dockerfile](https://training.lockedinlabs.ai/templates/fde-container-lab.Dockerfile)

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:

```text
*
!fde-container-lab.py
```

The 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.

```sh
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.

```sh
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/review
```

After 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.

```sh
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:

```sh
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:

```sh
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:

```sh
docker stop --timeout 1 lockedin-container-101
docker rm lockedin-container-101
```

Run 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:

1. One image ID and the container IDs before and after replacement.
2. The actual response after restart, replacement without a mount and replacement with a mount.
3. A diagram placing code, process memory, durable reviews and runtime credentials in different locations.
4. 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:

```sh
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-data
```

Use 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?](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-an-image/)
- [Docker: container storage and writable layers](https://docs.docker.com/engine/storage/)
- [Docker: named volumes and their lifecycle](https://docs.docker.com/engine/storage/volumes/)
- [Docker: running containers, port mappings and limits](https://docs.docker.com/engine/containers/run/)
- [Google Cloud Run: container filesystem contract](https://docs.cloud.google.com/run/docs/container-contract#file_system)
- [AWS: Amazon ECS storage options](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_data_volumes.html)
- [Azure Container Apps: storage mounts](https://learn.microsoft.com/en-us/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.
