Docker Part-1 — 1 Hour Session Plan
A one-hour walkthrough of Docker fundamentals — the problem containers solve, images vs containers, three live demos, six essential commands, and common questions.
This is the condensed one-hour version of the full Docker Part-1 guide. All commands can be run as you follow along.
The Problem Containers Solve
You write an application on your laptop. It works perfectly. You hand it to a colleague and they say it does not work on their machine. Same code. Different version of Node, different library version, different OS. The code is identical — the environment is different.
Scale that up: deploying to a cloud server means hours spent making the server look like your laptop. Add another server — repeat. A new developer joins — repeat again. This has a name: the works on my machine problem.
Containerisation solves this by packaging not just the code but the entire environment it needs to run — together, in one unit. That unit runs identically everywhere: your laptop, a colleague’s machine, a production server in Singapore. The environment travels with the code.
Virtual Machines vs Containers
Before Docker, the standard approach was Virtual Machines. The key difference:
1
2
3
4
5
6
7
8
9
VIRTUAL MACHINE CONTAINER
───────────────────── ─────────────────────
[ App A ] [ App B ] [ App A ] [ App B ]
[ OS ] [ OS ] ─────────────────────
───────────────────── [ Docker Runtime ]
[ Hypervisor ] ─────────────────────
───────────────────── [ Host OS ]
[ Host OS ] ─────────────────────
[ Hardware ] [ Hardware ]
A VM carries a full operating system with it — gigabytes of files just to run one application. A container shares the host machine’s OS kernel and only packages what the application needs on top of that.
| Virtual Machine | Container | |
|---|---|---|
| Includes full OS | Yes | No |
| Startup time | Minutes | Seconds |
| Size | Gigabytes | Megabytes |
| Isolation | Full OS boundary | Process-level |
Both have their place. VMs give stronger isolation. Containers give speed and portability. For most application deployment, containers win.
Image vs Container
An image is a static, read-only file on disk. It is a blueprint — it describes an environment: which OS files to include, which software to install, which command to run on startup. An image by itself does nothing. It just exists.
A container is what you get when you tell Docker to start an image. Docker takes the image, sets up an isolated environment, and starts the process the image defines. Now it is alive — using CPU and memory, doing something.
An image is a recipe. A container is the dish cooked from that recipe. The recipe never changes — you can cook the same dish a hundred times and get a hundred separate plates. Docker works the same way: run ten containers from the same image simultaneously, each completely independent.
The Writable Layer
When Docker starts a container it does not modify the image. It adds a thin writable layer on top — the container writes its files there, the image underneath stays untouched.
1
2
3
4
Container: [ Writable layer ] ← container writes here
[ App code layer ] ← read-only, from image
[ npm install ] ← read-only, from image
[ node:18-alpine ] ← read-only, from image
Ten containers can run from one image simultaneously because they all read the same shared layers, each with its own separate writable layer on top.
Container Lifecycle
1
docker run → [ Running ] → docker stop → [ Stopped ] → docker rm → gone
Stopping a container does not delete it — all its files are intact and it can be restarted. Removing it with docker rm is permanent. The image is completely unaffected by any of this.
Every docker run creates a brand new container. It never reuses a previous one. docker start restarts an existing stopped container. These are different commands with different purposes.
| Concept | Docker Equivalent |
|---|---|
| Class definition | Image |
| Object instance | Container |
new MyClass() |
docker run |
| Ten objects | Ten containers from one image |
The Ecosystem
Docker — the platform. Three parts: Docker Engine (the background service doing the actual work), Docker CLI (the docker command you type), and Docker Desktop (the GUI on Mac/Windows that bundles both). The CLI sends requests to the Engine; the Engine does the work.
Docker Hub — the public image registry. Think of it like npm but for container images. When you run docker run nginx, Docker downloads the nginx image from Docker Hub. Official images — nginx, postgres, node, python — are maintained by Docker or the software vendors themselves.
Podman — a Docker alternative from Red Hat, with no background daemon and rootless by default. Every command below works with podman instead of docker — it was designed as a drop-in replacement.
Colima — a free, open-source alternative to Docker Desktop for macOS and Linux, useful for organisations where Docker Desktop licensing is a constraint.
Demo 1 — hello-world
The simplest container available. Its only job is to print a message and exit.
1
docker run hello-world
docker run creates and starts a new container. hello-world is the image to use.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
719385e32844: Pull complete
Digest: sha256:a13ec89cdf897b3e551bd9f89d499db6ff3a7f44c5b9eb8bca40da20d4...
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
“Unable to find image locally” — Docker checked its local cache, found nothing, went to Docker Hub. Each “Pull complete” line is one image layer downloaded. The message at step 3 confirms the sequence: image existed first, container was created from it, process ran, printed, exited.
Run it a second time — the pull lines do not appear. Docker uses the cached image.
Demo 2 — Interactive Ubuntu
1
docker run -it ubuntu bash
-i keeps stdin open so you can type into the container. -t allocates a pseudo-terminal so the output looks like a proper shell. These two flags are almost always used together. ubuntu is the official Ubuntu image. bash is the command to run inside the container.
1
root@a3f7b2c91d4e:/#
That prompt is a bash shell inside the Ubuntu container — completely isolated from the host machine.
1
2
3
4
5
cat /etc/os-release # Ubuntu, even if the host is macOS
ps aux # only bash and ps — nothing else running
echo "hello" > /tmp/test.txt
cat /tmp/test.txt
exit
After exiting, running docker run -it ubuntu bash again creates a brand new container. The file written in /tmp/test.txt will not be there — every docker run is a fresh start. The old container still exists in a stopped state (docker ps -a shows it), but a new run gives a completely new environment.
Demo 3 — Nginx Web Server
1
docker run -d -p 8080:80 --name my-nginx nginx
-d runs the container in the background — the terminal returns immediately. -p 8080:80 maps port 8080 on the host machine to port 80 inside the container. Nginx listens on port 80 inside, but that port is not reachable from outside the container without this mapping. Format is always host-port:container-port. --name my-nginx assigns a human-readable name — without it, Docker assigns something random.
After running, open http://localhost:8080 in a browser. The Nginx welcome page loads.
One command. No installation steps, no config files, no version conflicts.
Six Essential Commands
All of these run against the nginx container started above.
docker ps
1
docker ps
1
2
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7c9b3d4a8f2e nginx "nginx…" 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp my-nginx
STATUS shows “Up 2 minutes” — the container is running. PORTS shows 0.0.0.0:8080->80/tcp — host port 8080 maps to container port 80.
1
docker ps -a
Shows all containers including stopped ones. The ubuntu and hello-world containers from the demos appear here with status Exited — they still exist on disk, just stopped.
docker logs
1
2
docker logs my-nginx # everything the container has printed so far
docker logs -f my-nginx # follow in real time — Ctrl+C to stop (container keeps running)
Refreshing the browser while following logs shows each request appear as an access log entry. This is the primary tool for seeing what a background container is doing.
docker exec
1
docker exec -it my-nginx bash
Runs a command inside an already-running container without stopping it. exec is different from run — run creates a new container, exec runs inside an existing one.
1
2
3
ls /etc/nginx/ # nginx config files inside the container
cat /usr/share/nginx/html/index.html # the HTML being served to the browser
exit # back to host — nginx is still running
docker stop
1
docker stop my-nginx
Sends SIGTERM to the main process, giving it time to shut down cleanly. Docker waits 10 seconds before forcing termination. Always prefer stop over kill for anything with data in flight.
1
2
3
docker ps # my-nginx no longer appears
docker ps -a # still exists, Status: Exited
docker start my-nginx # back up, same config, same port mapping
docker rm
1
2
docker stop my-nginx
docker rm my-nginx
rm permanently deletes the container and everything in its writable layer. No undo.
1
docker rm -f my-nginx # force: stop and remove in one step
To auto-remove a container the moment it exits, use --rm in docker run:
1
2
docker run --rm ubuntu echo "hello and goodbye"
# runs, prints, removes itself automatically
docker system prune
1
2
docker system df # where Docker's disk usage is coming from
docker system prune # removes stopped containers, unused images, networks, build cache
Does not touch running containers or named volumes. Safe to run anytime disk space needs reclaiming.
Common Questions
I ran docker run ubuntu and it immediately exited. Is something broken?
Nothing is broken. Ubuntu’s default command is bash. Bash exits when there is no terminal to interact with — without -i and -t, it has nothing to do and exits instantly. Fix: docker run -it ubuntu bash.
Every time I run docker run do I get a new container? What happened to the old one?
Yes — every docker run creates a fresh container. The previous one still exists stopped (docker ps -a shows it). To bring it back, use docker start. docker run always means a new container.
I deleted a container and my data is gone. How do I prevent this?
Anything written inside a container’s filesystem is lost when the container is removed. The solution is volumes — storage mounted from outside the container. The data lives in the volume, not in the writable layer. Delete the container, the volume and its data remain. Volumes are covered in the next session.
What to Practise
1
2
3
4
5
6
7
8
9
10
11
docker run hello-world
docker run -it ubuntu bash
docker run -d -p 8080:80 --name my-nginx nginx
docker ps
docker ps -a
docker logs -f my-nginx
docker exec -it my-nginx bash
docker stop my-nginx
docker start my-nginx
docker rm my-nginx
docker system prune
The full reference with detailed flag explanations, expected output, and further reading is in the Docker Part-1 Hands-On Guide.
Next session: Dockerfile — building your own images instead of running other people’s.