This is an old revision of the document!
Docker Introduction
- If you are like me and a late-comer to the Docker party, the following pages should help you find your way around in the Docker world.
Read this section every day for the next couple of days for some of the buzzwords to stick.
What is Docker
- Docker is an open-source platform that simplifies building, shipping, and running applications by using containerization.
- It packages an application with everything it needs to run—code, runtime, libraries, and system tools—into a single, portable unit called a container.
Images and Containers
There are two primary items that can trip you up.
- An Image.
- A Container.
- The image comes first and are used to produce one or more containers.
- Image = recipe / blueprint.
- Container = actual meal / house built from that blueprint.
Docker Image
- Read-only template.
- Contains the OS, code, libraries, env variables — everything needed to run.
- You build it or pull it: (e.g. docker pull nginx)
- It sits there, does nothing until you run it.
- You can make 100 copies from one image.
- Like a class in programming.
Docker Container
- Running instance of an image.
- When you do docker run nginx — Docker creates a container from the nginx image.
- It's alive, has its own process, file system, IP, can be started, stopped, deleted.
- You can change stuff inside a container, but those changes don't affect the image unless you commit them.
- Like an object created from a class.
Docker Image and Container Relationship
docker run <image> -> creates a -> container
Analogy:
- Image is like an ISO file for Ubuntu.
- Container is Ubuntu actually installed and running on your server.
- You can install Ubuntu 10 times from the same ISO.
Layers and Volumes
Layers
- Each Docker image consist of various layers.
- This makes images small and fast.
- A Docker image is NOT one big file.
- It's a stack of layers, like a lasagna.
- Each instruction in a Dockerfile = 1 new layer.
- Lets look at a simple sample Dockerfile:
FROM ubuntu # Layer 1 - base OS RUN apt install python # Layer 2 - adds python COPY app.py . # Layer 3 - adds your code CMD python app.py # Layer 4 - what to run
Why this is genius:
- Cache: If you only change app.py, Docker reuses Layer 1 and 2. It only rebuilds Layer 3. Super fast.
- Sharing: If you have 10 images all FROM ubuntu, you only store that ubuntu layer once on disk.
- Read-only: Image layers are read-only.
Running a Container
- When you run a container, Docker adds one thin writable layer on top.
- All your changes inside the container go there.
- When you delete the container, that writable layer is gone.
- The image layers stay clean.
- The image thus typically contains a couple of layers.
- When this image is used to produce a container, another writable layer is added specific as part of the container.
[ Writable Container Layer ] <- you write here, deleted when container dies [ Layer 3 - app.py ] <- read-only [ Layer 2 - python ] <- read-only [ Layer 1 - ubuntu ] <- read-only
- That’s why docker run is instant. It doesn't copy anything, it just adds a writable layer.
Volumes — where data actually lives
- Problem: If container layer dies when container dies, where do you keep databases, uploaded files, etc?
- A volume is a folder on your host machine that is mounted into the container. It lives outside the container lifecycle.
# Without volume - data dies with container docker run postgres # With volume - data survives docker run -v mydata:/var/lib/postgresql/data postgres
- mydata lives on your host in /var/lib/docker/volumes/
- Container writes to /var/lib/postgresql/data → actually writes to mydata on host.
- Delete container → mydata still there.
- Start new container with same volume → your DB is still there.
3 types you'll see:
- Named volume mydata:/app/data - managed by Docker, best for databases.
- Bind mount ./mycode:/app - you link your local folder to container. Great for development, edit code locally, container sees it instantly.
- Anonymous volume - Docker creates random name, don't use it.
Short Recap
- Image layers = how the app is built (efficient, shared, read-only)
- Container writable layer = temporary scratch space (dies)
- Volume = permanent memory outside the container (survives)
- Image = blueprint
- Layers = pages of the blueprint
- Container = house built from blueprint + a whiteboard inside (writable layer)
- Volume = external garage where you store stuff so it doesn't get lost when you demolish the house.
Dockerfile and docker-compose.yml
- Dockerfile = how to BUILD one image
- docker-compose.yml = how to RUN many containers together
Dockerfile
- Recipe to create an image. One file = one image.
FROM python:3.11 WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD ["python", "app.py"]
- You build it:
docker build -t my-app .
- Result: an image called my-app.
- It knows nothing about ports, volumes, networks, other containers, environment.
- It only cares about what's inside that one container.
docker-compose.yml
- Blueprint for your whole stack. How containers connect and run together.
services: web: build: . # <- uses your Dockerfile to build ports: - "5000:5000" volumes: - .:/app depends_on: - db networks: - app-net db: image: postgres:15 # <- uses existing image from Docker Hub environment: POSTGRES_PASSWORD: secret volumes: - pgdata:/var/lib/postgresql/data networks: - app-net networks: app-net: volumes: pgdata:
- You run it:
docker compose up
- Result: 2 containers running, connected on same network, with volume.
- It doesn't build anything new (unless you tell it build: .). It just orchestrates.
Analogy:
- You're building a house.
- Dockerfile = instructions for how to build one brick type — what clay, how hot, how long in oven.
- docker-compose.yml = architectural plan — where bricks go, where plumbing and electrical go, how 3 houses connect into a neighborhood.
When to use what:
- You write a Dockerfile once per app/service you create.
- You write a docker-compose.yml once per project to run all your services together locally.
You can use Dockerfile without compose:
- docker build + docker run
You cannot use compose without images — either you build them via Dockerfile, or pull them from Hub.
- In short:
- Dockerfile → docker build → Image
- docker-compose.yml → docker compose up → Running containers (built from Dockerfiles or Hub images)