Level 8 / Packaging runtime

Containers & Docker 0 to Hero

Learn Docker and containers from first principles: image versus container, Dockerfile, layers, ports, volumes, logs, debugging, cleanup, and the exact reason containers fix the classic “works on my machine” problem.

  • Why containers matter Package app code together with the runtime, dependencies, and startup behavior.
  • What you will practice Building images, running containers, reading logs, mounting volumes, and cleaning up safely.
  • How this page helps Use it as both a learning path and a practical Docker reference while you work locally.
Dockerfile
Image
Registry
Container
Cleanup

Container stack

Dockerfile The recipe that explains how to build a reusable image.
build instructions
Image layers A frozen blueprint of the app plus dependencies and defaults.
read only
Container A running process created from the image with a small writable layer on top.
runtime

VM versus container

VM
app
libs
guest OS
hypervisor + host
container
app
libs
container engine
host OS kernel

Learn Docker in the order that makes local practice easy

Docker gets much easier when you learn the runtime story first, then the Dockerfile, then the day-to-day commands. The goal is to build confidence without filling your machine with mystery containers.

01

Understand the nouns

Dockerfile, image, registry, container, port mapping, bind mount, and volume should all feel distinct.

02

Build one image

Write a tiny Dockerfile, build the image, and inspect the result before running it.

03

Run and inspect

Learn docker run, ps, logs, and exec until they feel normal.

04

Handle data and config

Use ports, environment variables, bind mounts, and named volumes without guessing.

05

Clean up on purpose

Stop and remove containers, prune safely, and leave your machine in a known-good state.

The shortest useful definition of a container is: a running process packaged with the filesystem and defaults it needs, isolated from other processes by the host OS.

That is why Docker is so practical. You stop depending on the host machine being set up “just right” and instead ship the runtime with the app.

When Docker helps most

Use Docker when you need consistent runtimes across machines, fast onboarding, reproducible builds, or a clean way to package an app for CI and production.

See the Docker flow before you memorize flags

Most Docker confusion disappears once you can picture the full lifecycle from source code to running container. That flow is what the CLI is helping you control.

1 Write a Dockerfile

Describe the base image, dependencies, files, environment variables, and startup command.

2 Build an image

Docker executes the steps and creates reusable image layers.

3 Tag and store

Optionally tag the image and push it to a registry for reuse elsewhere.

4 Run a container

Docker launches a process from the image and adds a small writable runtime layer.

5 Inspect behavior

Use logs, exec, inspect, and port mapping to understand what the container is doing.

6 Stop and clean up

Remove containers, images, and unused resources when you are finished.

Know why containers feel lighter than virtual machines

A VM includes a whole guest operating system. A container shares the host kernel and packages only the app, dependencies, and runtime surface it needs.

Virtual machine

Heavier but full OS isolated

application
libraries
guest operating system
hypervisor + host infrastructure

Great when you need a full guest OS, but slower to boot and heavier on resources.

Container

Lighter and faster to start

application
libraries
container engine
host OS kernel

Excellent for consistent app packaging because you avoid shipping a full guest OS every time.

Read a Dockerfile like a build pipeline, not just a text file

A Dockerfile is the recipe that becomes an image. Each instruction adds a layer or changes how the image will behave at runtime.

Starter DockerfilePython example
FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

ENV PORT=8000
EXPOSE 8000

CMD ["python", "app.py"]
FROM

Picks the base image you are building on top of. This is the runtime starting point.

WORKDIR

Sets the working directory for later instructions and the default runtime location.

COPY + RUN

Bring files into the image and install dependencies during the build.

ENV / EXPOSE / CMD

Define runtime defaults, document network intent, and set the default startup command.

The Docker commands you will use over and over

Search or filter the command atlas below. The goal is not only to list commands, but to make it obvious which one to reach for during build, run, inspect, data handling, or cleanup.

24 rows
Command What it does When to use it
docker version Shows Docker client and server version details. Run it first when you want to confirm Docker is installed and reachable.
docker run --rm hello-world Runs a tiny test container and removes it afterward. Great quick proof that your Docker runtime works.
docker build -t my-app:dev . Builds an image from the Dockerfile in the current directory and tags it. Use it after changing app code or Dockerfile instructions.
docker images Lists local images. Use it to confirm the image exists and to inspect tags and sizes.
docker pull nginx:alpine Downloads an image from a registry. Use it when you want to run or inspect an image without building one yourself.
docker run -d --name web -p 8080:80 nginx Runs the nginx image in detached mode and maps container port 80 to local port 8080. Use it for services you want to keep running in the background.
docker ps Lists running containers. Use it to verify that a container is still up and what ports are exposed.
docker ps -a Lists all containers, including stopped ones. Useful when a container exits too quickly and disappears from normal ps.
docker logs -f web Streams logs from the web container. Use it when the app starts but you need runtime clues.
docker exec -it web sh Starts an interactive shell inside the running container. Helpful for checking files, environment variables, and in-container behavior.
docker inspect web Shows detailed JSON metadata for the container. Use it when you need exact mounts, env vars, IPs, or port bindings.
docker stop web Stops a running container. Use it when you want the container shut down cleanly.
docker start web Starts a previously stopped container. Useful when you want to reuse an existing container instead of creating a new one.
docker rm web Removes a stopped container. Use it after stopping a container that you no longer need.
docker rmi my-app:dev Deletes a local image by tag. Use it when you want to reclaim space or reset a local image build.
docker tag my-app:dev myrepo/my-app:dev Creates another tag pointing to the same image. Common before pushing to a remote registry.
docker push myrepo/my-app:dev Uploads an image tag to a registry. Use it when you want other machines or CI systems to pull the image.
docker run --rm -e APP_MODE=demo my-app:dev Runs a container with an environment variable override. Use it for runtime config that should not be baked into the image.
docker run --rm -v "$PWD":/app -w /app node:20 npm test Mounts the current directory into the container and runs a command there. Useful for ad hoc tooling or local dev workflows.
docker volume create app-data Creates a named volume for persistent container data. Use it when data should survive container replacement.
docker volume ls Lists named volumes. Use it to see what persistent local data Docker is managing.
docker run -d --name db -v app-data:/var/lib/postgresql/data postgres:16 Runs a database container with its data stored in a named volume. Good basic example of durable data outside the container filesystem.
docker network ls Lists Docker networks on the host. Helpful when you are debugging connectivity between containers.
docker system df Shows disk usage for images, containers, and volumes. Use it before cleanup if Docker is consuming more space than expected.
docker container prune -f Deletes all stopped containers. Fast cleanup for old lab containers you forgot to remove individually.
docker image prune -f Deletes dangling images. Useful after repeated rebuilds that leave untagged layers behind.
docker system prune -f Removes unused containers, networks, and dangling images. Use it carefully when you want an aggressive cleanup of unused Docker state.

Tip: pair docker ps -a, docker logs, and docker inspect before rebuilding. Many container problems are runtime configuration issues, not build issues.

Safe Docker drills you can run on your own machine

These labs are intentionally simple and local. The point is to make images, containers, ports, logs, mounts, and cleanup feel normal rather than mysterious.

Best first step

Hello world

Confirm the runtime works before you do anything more complicated.

Best web demo

Nginx

Practice detached containers, port mapping, logs, and browser testing.

Best build drill

Custom image

Write a tiny Dockerfile and turn code into a repeatable runnable package.

Best cleanup habit

Remove what you create

Stop, remove, prune, and leave your machine predictable for the next lab.

Lab 0

Verify Docker is working

Confirm the Docker client can talk to the runtime and run a disposable test container.

5 min
RunRuntime check
docker version
docker run --rm hello-world
Lab 1

Run a web server and expose it on your laptop

Practice detached mode, naming, port mapping, and container logs with a tiny nginx container.

10 min
RunWeb server
docker run -d --name web -p 8080:80 nginx
docker ps
docker logs web
Lab 2

Build your own image from a Dockerfile

Take a tiny app or static site and turn it into a reusable image.

15 min
RunBuild + run
docker build -t my-app:dev .
docker images
docker run --rm my-app:dev
Lab 3

Run with environment variables and inspect logs

See how runtime configuration changes behavior without rebuilding the image.

10 min
RunConfig + logs
docker run --rm -e APP_MODE=demo my-app:dev
docker logs -f <container-name>
Lab 4

Use a bind mount for local development

Mount your local project into a container so the container sees live files from your machine.

12 min
RunBind mount
docker run --rm -v "$PWD":/app -w /app node:20 npm test
Lab 5

Persist data with a named volume

See how data survives container replacement when it lives in a Docker volume instead of the container filesystem.

15 min
RunNamed volume
docker volume create app-data
docker run -d --name db -v app-data:/var/lib/postgresql/data postgres:16
docker volume ls

Debug container problems in the right order

With Docker, the fastest path is usually: check whether the container is still running, read the logs, inspect the config, and only then rebuild if you truly need to.

Fast debug sequence

Check running state

Use docker ps or docker ps -a first.

state
Read the logs

Use docker logs before rebuilding. Startup errors often explain themselves.

logs
Inspect the container

Use docker inspect for ports, mounts, env vars, and entrypoint details.

inspect
Enter the container

Use docker exec when you need to inspect files or runtime environment from inside.

exec

Common signals and what they usually mean

Signal Likely meaning First move
Container exits immediately The main process finished, crashed, or the startup command is wrong. docker ps -a then docker logs
Port already allocated Something else on your host is already using that port. Pick another host port or stop the conflicting process
Image builds but app still fails The problem is likely runtime config, startup logic, or environment, not the build itself. docker logs and docker inspect
Data disappeared after restart The data lived inside the container filesystem, not a named volume. Move state into a volume
App cannot reach localhost Inside a container, localhost means the container itself, not your laptop. Recheck networking assumptions and host mapping
Permission denied File permissions, user IDs, or mount ownership may not match what the container expects. Inspect mounts and the container user

See why containers solve “works on my machine”

This simulator keeps the core idea simple: raw code fails on a mismatched production host, while a container succeeds because it brings the runtime with it.

Laptop Python3.9
Server Python3.6
Package moderaw code
Resultwaiting
Write app locally with Python 3.9 features.
Package it either as raw files or as a container image.
Ship it to a production server with older runtime defaults.
Observe the crash or success path.

Event log

Environment view

Dev machine

My laptop

OSmacOS
Python3.9
Dependenciesinstalled
🐍
Prod host

Old server

OSLinux
Python3.6
Dependenciesmissing
Deployment zone waiting for package...

Keep your Docker machine clean and predictable

Docker is easiest to trust when you know exactly how to stop, remove, and prune what you created. Cleanup is part of the skill, not an afterthought.

Fast cleanup checklist

  • Stop running containers you no longer need: docker stop ....
  • Remove stopped containers: docker rm ... or docker container prune -f.
  • Delete local images when you want a clean rebuild: docker rmi ....
  • Remove named volumes explicitly if you no longer want the data.

What to be careful with

  • docker system prune -f is aggressive. Use it only when you understand what is considered unused.
  • Named volumes can outlive containers on purpose. That is a feature, not a bug.
  • Stopping a container is not the same as removing it. Removed containers free mental space.

Your next step after Docker starts feeling easy

Once Docker feels predictable, the natural progression is learning how to manage many containers at once and how to package those deployments cleanly.

Practice next

  • Build one app image and one database container with a named volume.
  • Use logs and exec to diagnose a failing startup without rebuilding immediately.
  • Tag and push an image to a registry, then pull it on another machine.
  • Compare bind mounts and named volumes until the difference feels obvious.

Related pages in this hub

Containers are the foundation layer. The pages below show what comes next.

  • Kubernetes for orchestrating many containers at once.
  • Helm for packaging Kubernetes installs into reusable releases.
  • CI/CD for automating image builds and deployments.