Docker Lesson 8 – Docker Containers Explained | Dataplexa
Section I · Lesson 8

Docker Containers Explained

An image is the blueprint. A container is what happens when that blueprint comes to life. This lesson goes deep on what a container actually is, how it stays isolated from everything else on your machine, and what its lifecycle really looks like.

Most developers use containers for months before they truly understand what they are. That gap causes confusion when things go wrong — a container mysteriously loses data, two containers can't talk to each other, a container exits immediately after starting. Every one of those problems makes sense once you understand containers at the level this lesson covers.

A Container Is an Isolated Process

Here's the most important thing to understand about containers — and the thing most tutorials skip over: a container is not a mini-VM. A container is a regular Linux process — or a group of processes — running on the host machine, but wrapped in isolation using the Linux kernel features you saw in Lesson 3: namespaces and cgroups.

From the container's perspective, it thinks it has the machine to itself — its own process tree, its own network interfaces, its own filesystem, its own hostname. From the host's perspective, it's just another set of processes with some kernel-level guardrails around them.

The Office Cubicle Analogy

A container is like a cubicle in an open-plan office. The person in the cubicle has their own desk, their own monitor, their own phone — their own isolated workspace. They can't see what their neighbours are doing. They can't reach across and touch their neighbour's keyboard. But they're all still in the same building, sharing the same electricity, the same air conditioning, the same internet connection. The building is the host OS. The cubicle walls are namespaces. The electricity budget per desk is cgroups. The office worker is your application process.

The Writable Layer

In Lesson 7 you saw that images are read-only stacks of layers. So how does a container write files? The answer is the container layer — a thin, writable layer that the Docker Daemon adds on top of the image's read-only layers the moment a container is created.

Every file the container creates, modifies, or deletes goes into this writable layer. The read-only image layers beneath are never touched. This is called copy-on-write — if a container needs to modify a file that exists in the image layers, Docker copies that file up into the writable layer first, then modifies the copy. The original in the image layer is left intact.

Container layer on top of image layers

Base OS layer
Runtime layer
App code layer
Writable container layer ✎
Container A
Base OS layer
Runtime layer
App code layer
Writable container layer ✎
Container B

Both containers share the same three read-only image layers. Each has its own independent writable layer. Changes in Container A never affect Container B — or the shared image layers beneath.

Container Storage Is Ephemeral

Everything written to the container's writable layer disappears when the container is deleted. A database container that stores its data in the writable layer loses all its data the moment you run docker rm. This is why Docker Volumes exist — and why Lesson 15 is one of the most important lessons in this course. For now, remember: containers are temporary. Data you care about must live outside them.

Container States

A container moves through a defined set of states during its lifetime. Understanding these states explains every status you'll see in docker ps output.

Container lifecycle states

Created — container exists but the process has not started yet
↓ docker start
Running — the main process is active and the container is doing its job
↓ docker pause
↓ docker stop
↓ process exits
Paused

process is frozen, memory preserved

Stopped

process ended, container still exists

Exited

process finished or crashed

↓ docker rm
Deleted — container and its writable layer are permanently removed

The distinction between Stopped and Deleted trips up a lot of beginners. A stopped container still exists — it still has its writable layer, its name, its configuration. You can restart it with docker start and it picks up exactly where it left off. A deleted container is gone permanently — writable layer and all. This is also why docker ps only shows running containers, and docker ps -a shows stopped ones too.

Containers Are Stateless by Design

The best containers are stateless — they don't store anything they care about inside themselves. They receive input, do their work, produce output, and could be killed and replaced with a fresh copy at any moment without any data loss.

This isn't a limitation — it's a superpower. A stateless container can be scaled horizontally by spinning up ten identical copies behind a load balancer. If one crashes, the orchestrator starts a replacement in seconds. If you need to deploy a new version, you swap the old containers for new ones with zero downtime. None of this works if containers are hoarding state internally.

State that needs to persist — database files, uploaded files, session data — goes into a Docker Volume (Lesson 15) or an external service like S3 or a managed database. The container becomes a replaceable, ephemeral compute unit. That's the design philosophy behind every modern containerised application.

Inspecting a Running Container

The scenario: You're an SRE and a container in staging is behaving oddly. It's running, but requests are failing intermittently. You need to inspect exactly what the container looks like from the inside — its configuration, its environment variables, its network settings — without stopping it.

docker run -d --name frontend-app -p 3000:3000 frontend-app:v2.1.0
# start the container first so we have something to inspect

docker inspect frontend-app
# returns the full JSON configuration of the container:
# IP address, port bindings, mounts, environment variables,
# restart policy, network settings, and much more
[
  {
    "Id": "a3f2c8d91e44b7e1a4c52f889201cd3f",
    "Name": "/frontend-app",
    "State": {
      "Status": "running",
      "Running": true,
      "Pid": 14823
    },
    "NetworkSettings": {
      "IPAddress": "172.17.0.2",
      "Ports": {
        "3000/tcp": [{ "HostIp": "0.0.0.0", "HostPort": "3000" }]
      }
    },
    "Mounts": [],
    "Config": {
      "Env": [
        "NODE_ENV=production",
        "PORT=3000"
      ],
      "Cmd": ["node", "server.js"],
      "Image": "frontend-app:v2.1.0"
    }
  }
]

What just happened?

docker inspect dumps the full runtime state of the container as JSON. The State block confirms it's running and gives you the host process ID — you can use that PID to inspect it at the OS level if needed. The NetworkSettings block shows the container's internal IP address (172.17.0.2) and the port binding. The Config block reveals the environment variables baked in at startup — this is how you verify that NODE_ENV is actually set to production and not accidentally left as development. The Cmd field shows exactly what process is running as PID 1 inside the container.

Teacher's Note

docker inspect is the first command I run when a container isn't behaving — environment variables set wrong and port bindings misconfigured account for the majority of container bugs in real projects.

Practice Questions

1. The mechanism by which Docker copies a file from an image layer into the container's writable layer before modifying it is called what?



2. To see a container's full JSON configuration — including its IP address, environment variables, and port bindings — which command do you run?



3. A container that stores no internal state and can be killed and replaced at any moment without data loss is called what?



Quiz

1. At the operating system level, a Docker container is best described as what?


2. You run docker stop my-container. What is the state of the container afterwards?


3. A database container stores its data inside its own writable layer. What happens to that data when the container is deleted?


Up Next · Lesson 9

Docker Lifecycle

You know the states — now let's walk the full lifecycle of a container from creation to deletion, and see every transition command in a real workflow.