0% found this document useful (0 votes)
2 views38 pages

Docker-Complete-Guide

This document serves as a comprehensive guide to Docker for backend engineers, covering topics such as the differences between Docker and virtual machines, installation on Ubuntu, and practical applications like containerizing FastAPI with PostgreSQL. It outlines the problems Docker solves, such as environment inconsistency and dependency conflicts, and provides a structured format for each section, including practical commands and interview questions. The guide emphasizes Docker's benefits, including consistency, isolation, portability, and speed, making it essential for modern software development.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views38 pages

Docker-Complete-Guide

This document serves as a comprehensive guide to Docker for backend engineers, covering topics such as the differences between Docker and virtual machines, installation on Ubuntu, and practical applications like containerizing FastAPI with PostgreSQL. It outlines the problems Docker solves, such as environment inconsistency and dependency conflicts, and provides a structured format for each section, including practical commands and interview questions. The guide emphasizes Docker's benefits, including consistency, isolation, portability, and speed, making it essential for modern software development.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

CONTAINERIZATION

Docker

From Fundamentals to Production,


for Backend Engineers

Topics: Why Docker · Docker vs VMs · Images vs Containers


Dockerfile & Layer Caching · Docker Compose · Networks & Volumes
Docker Hub · CI/CD · Multi-Stage Builds · Kubernetes Bridge

Includes: Containerizing FastAPI + PostgreSQL end to end


How to Use This Document

Every section follows the same pattern:

1. The Problem — what goes wrong without this


2. The Idea — plain language plus a real-life analogy
3. The Architecture — a diagram showing where it fits
4. The Practical — commands and files you type yourself
5. Interview Answers — how to say it out loud

If you can draw the lifecycle diagram (Dockerfile → Image → Container) from memory, you already understand most of Docker.

Table of Contents

Part Topic

0 Why Docker Exists

1 Docker vs Virtual Machines & Architecture

2 Installing Docker on Ubuntu

3 Image vs Container — the core concept

4 Dockerfile — building your own image

5 Containerizing FastAPI

6 Docker Compose — multi-container projects

7 Networks & Volumes

8 Docker Hub — publishing your images

9 Docker in a Real Project: CI/CD and Kubernetes

10 Advanced but Interview-Worthy

11 Cheat Sheet, Troubleshooting & Interview Bank

Docker — Complete Guide Page 2 of 38


Part 0 — Why Docker Exists

0.1 The Problem

I clone your project:

git clone second-brain

Then I ask: "How do I run it?"

You reply:

Install Python 3.12



Install PostgreSQL 18

Install [Link] 22

Install npm packages

Create a virtual environment

pip install -r [Link]

Create the database and user

Edit .env

Run the backend

Run the frontend

That is painful — and it is only the happy path. In reality:

I have Python 3.10, your code needs 3.12


I have PostgreSQL 15, your migration needs 18
A package installs fine on your Ubuntu and fails on my macOS
It works for you and breaks for me

This has a famous name:

"But it works on my machine!"

That sentence is the single biggest reason Docker exists.

0.2 The Docker Idea

Instead of sending instructions for building the environment, send the whole environment.

WITHOUT DOCKER WITH DOCKER

You send: [Link] You send: a Docker image


+ (that's all)
a 10-step README
Image already contains:
I must install: - Linux
- the right OS - Python 3.12
- the right Python - FastAPI, SQLAlchemy
- the right packages - all requirements
- configure everything - your code
- the start command
Result: maybe it works
Result: it always works

0.3 Real-Life Analogy — Shipping a House

Docker — Complete Guide Page 3 of 38


Without Docker — you send a blueprint:

Blueprint → buy bricks → buy cement → buy pipes → build

Everyone builds slightly differently. Some houses leak.

With Docker — you send a fully furnished house on a truck:

House → Truck → Destination → Unload → Live in it

No construction. Nothing to interpret. Identical result every time.

This is not a coincidence: Docker's logo is a whale carrying shipping containers. Before standardised shipping containers, every ship
was loaded differently and cargo handling was chaos. One standard box changed global trade. Docker did the same thing for software.

0.4 What You Are Working Towards

Today you run:

# terminal 1
sudo systemctl start postgresql
# terminal 2
source .venv/bin/activate && uvicorn [Link]:app --reload
# terminal 3
npm run dev

After Docker:

docker compose up

That is it. PostgreSQL, FastAPI, and [Link] all start, already configured, already connected.

0.5 Why Companies Care

Companies do not deploy code. They deploy Docker images.

Developer's laptop

Docker Image ← the exact same artifact, byte for byte

┌────┴─────┬──────────┬────────────┐
▼ ▼ ▼ ▼
Testing Staging Production Your teammate
(AWS) (Azure) (GCP) (their laptop)

The image that passed your tests is literally the image running in production. No "the staging server has a different OpenSSL version"
surprises.

Now scale that up. Imagine Google with thousands of servers. Installing Ubuntu, Python, Node, Java, and libraries manually on each one is
impossible. Instead: build one image, deploy it to server 1, server 2, ... server 10,000. Identical environment everywhere.

0.6 The Four Things Docker Actually Buys You

Benefit Meaning

Consistency Same environment on every machine — dev, test, prod

Isolation Project A can use Python 3.9 and Project B Python 3.12, on one laptop, with no conflict

Portability Runs on Ubuntu, macOS, Windows, AWS, Azure, GCP without changes

Speed A container starts in ~1 second; onboarding a new developer takes one command instead of a day

0.7 Interview Questions

Docker — Complete Guide Page 4 of 38


Q1. What is Docker? A platform for packaging an application together with all of its dependencies, libraries, and configuration into a
portable unit called a container, so it runs identically on any machine that has Docker installed.

Q2. What problem does Docker solve? Environment inconsistency — the "works on my machine" problem — along with dependency
conflicts between projects, slow onboarding, and differences between development and production environments.

Q3. Why do companies deploy images rather than source code? Because the image is a fixed, tested artifact. Deploying source code
means rebuilding the environment on each server, which can introduce differences; deploying an image guarantees that what was tested is
exactly what runs.

Docker — Complete Guide Page 5 of 38


Part 1 — Docker vs Virtual Machines & Architecture

1.1 The Question Every Interviewer Asks

"Docker sounds like a virtual machine. What is the difference?"

Both give you isolation. The difference is what they virtualise.

VIRTUAL MACHINES DOCKER CONTAINERS

┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐


│ App │ │ App │ │ App │ │ App │ │ App │ │ App │
├──────┤ ├──────┤ ├──────┤ ├──────┤ ├──────┤ ├──────┤
│ Libs │ │ Libs │ │ Libs │ │ Libs │ │ Libs │ │ Libs │
├──────┤ ├──────┤ ├──────┤ └──────┘ └──────┘ └──────┘
│Guest │ │Guest │ │Guest │ ┌──────────────────────────┐
│ OS │ │ OS │ │ OS │ ← heavy │ Docker Engine │
├──────┴─┴──────┴─┴──────┤ ├──────────────────────────┤
│ Hypervisor │ │ Host OS │
├─────────────────────────┤ ├──────────────────────────┤
│ Host OS │ │ Hardware │
├─────────────────────────┤ └──────────────────────────┘
│ Hardware │
└─────────────────────────┘

The key line: a VM ships an entire guest operating system. A container shares the host's kernel and ships only the application plus
its libraries.

Virtual Machine Docker Container

Virtualises Hardware The operating system

Contains a full OS ✔ Yes ✘ No — shares the host kernel

Size Gigabytes Megabytes

Startup time Minutes Seconds or less

How many on a laptop A few Dozens

Isolation strength Stronger (full separation) Strong, but shares the kernel

One-line answer:

"A VM virtualises hardware and runs a complete guest OS, so it is heavy and slow to boot. A container virtualises at the OS level,
sharing the host kernel through namespaces and cgroups, so it is lightweight and starts almost instantly. Containers give you isolation
at a fraction of the cost."

1.2 How Containers Are Actually Isolated

Two Linux kernel features do the work. Knowing their names impresses interviewers:

Feature What it does

Namespaces Isolate what a process can see — its own process list, network interfaces, filesystem, hostname

cgroups (control groups) Isolate what a process can use — CPU, memory, and I/O limits

A container is really just a normal Linux process that has been given its own private view of the system. It is not a tiny virtual
machine. That is why it starts as fast as any other process.

1.3 Docker's Architecture

Docker — Complete Guide Page 6 of 38


YOU
│ docker build / docker run / docker ps

┌─────────────────┐
│ Docker CLI │ the command you type
└─────────────────┘
│ REST API (over a Unix socket)

┌─────────────────────────────────────────────┐
│ Docker Daemon (dockerd) │ the background service
│ │
│ builds images · runs containers │
│ manages networks · manages volumes │
└─────────────────────────────────────────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────────┐
│ containerd│ │ Docker Hub │ the remote registry
│ + runc │ │ (registry) │
└──────────┘ └──────────────┘
actually starts
the container

Component Role

Docker CLI The docker ... commands you type

Docker Daemon ( dockerd ) The service that does all the real work

containerd / runc The low-level runtime that actually creates the container process

Registry (Docker Hub) Where images are stored and shared

This is why Docker fails with "Cannot connect to the Docker daemon" — the CLI is fine, but the background service is not running. The
fix is sudo systemctl start docker .

1.4 Interview Questions

Q1. Docker vs Virtual Machine? A VM virtualises hardware and runs its own guest OS, making it heavy (GBs, minutes to boot). A
container virtualises the OS, sharing the host kernel, making it light (MBs, near-instant start). VMs give stronger isolation; containers give
far better density and speed.

Q2. How does a container achieve isolation? Through Linux namespaces, which give the process its own view of the filesystem,
network, and process tree, and cgroups, which limit its CPU, memory, and I/O usage.

Q3. What is the Docker daemon? The background service ( dockerd ) that builds images, runs containers, and manages networks and
volumes. The CLI is only a client that sends it API requests.

Docker — Complete Guide Page 7 of 38


Part 2 — Installing Docker on Ubuntu

Install from Docker's official repository, not the outdated [Link] package in Ubuntu's default repos. This is how it is done
professionally, and it gets you current versions plus Compose v2.

2.1 Check First

docker --version
docker compose version

If both print versions, skip to Part 3.

2.2 The Installation

Step 1 — Remove old packages (safe if none exist)

sudo apt remove docker docker-engine [Link] containerd runc

Step 2 — Update

sudo apt update

Step 3 — Install prerequisites

sudo apt install -y ca-certificates curl gnupg lsb-release

These let Ubuntu securely fetch and verify packages from Docker.

Step 4 & 5 — Add Docker's official GPG key

sudo mkdir -p /etc/apt/keyrings

curl -fsSL [Link] | \


sudo gpg --dearmor -o /etc/apt/keyrings/[Link]

Why a GPG key? It cryptographically proves the packages really came from Docker and were not tampered with in transit. This is the
same trust model as a JWT signature.

Step 6 — Permissions

sudo chmod a+r /etc/apt/keyrings/[Link]

Step 7 — Add Docker's repository

echo \
"deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/[Link]] \
[Link] \
$(. /etc/os-release && echo $VERSION_CODENAME) stable" | \
sudo tee /etc/apt/[Link].d/[Link] > /dev/null

This tells Ubuntu: "from now on, get Docker directly from Docker."

Step 8 — Update again

sudo apt update

Step 9 — Install

sudo apt install -y docker-ce docker-ce-cli [Link] \


docker-buildx-plugin docker-compose-plugin

Docker — Complete Guide Page 8 of 38


Package What it is

docker-ce Docker Engine (the daemon)

docker-ce-cli The docker command

[Link] The container runtime

docker-buildx-plugin Modern, faster image builder

docker-compose-plugin docker compose (v2, a subcommand — not the old docker-compose script)

Step 10 — Start and enable

sudo systemctl enable docker # start automatically on boot


sudo systemctl start docker

Step 11 — Verify

docker --version # Docker version 28.x.x


docker compose version # Docker Compose version v2.x.x

2.3 Step 12 — Run Docker Without sudo ★

By default Docker needs root. Add yourself to the docker group:

sudo usermod -aG docker $USER


newgrp docker # apply without logging out

Now docker ps works without sudo .

⚠ Security note worth knowing for interviews: membership of the docker group is effectively root access, because you can
mount the host filesystem into a container. It is fine on your own development laptop; on a shared production server you would use
rootless Docker or restrict access.

2.4 The Hello World

docker run hello-world

Hello from Docker!


This message shows that your installation appears to be working correctly.

This is Docker's equivalent of your first FastAPI server.

2.5 What You Just Installed

Docker Engine → runs containers


Docker CLI → the docker ... commands
Docker Compose → runs multi-container projects

Compose is here from the start because your project will end up looking like this:

Docker Compose

┌───────────────┼────────────────┐
▼ ▼ ▼
FastAPI PostgreSQL [Link]
Container Container Container

Three services, one command.

Docker — Complete Guide Page 9 of 38


Part 3 — Image vs Container

If you understand this Part, you understand 70% of Docker. Every Docker interview starts here.

3.1 What Actually Happened in docker run hello-world

Look at the output carefully — Docker narrated its own lifecycle:

Unable to find image 'hello-world:latest' locally


Pulling from library/hello-world
Downloaded newer image for hello-world:latest

So Docker did not run hello-world directly. It did this:

Docker Hub

docker pull (automatic)


Image on your disk

create container


Container

execute


"Hello from Docker!"


Container exits

That is Docker's entire lifecycle. Everything else is detail.

3.2 The Analogy You Should Never Forget ★

You already know Python OOP:

class Student: # a blueprint — nothing is running


pass

s1 = Student() # an object — now something exists in memory


s2 = Student()
s3 = Student()

PYTHON DOCKER
------ ------
Class ←→ Image
Object ←→ Container

One class, One image,


many objects many containers

This single analogy answers most interview questions on the topic.

3.3 What Is an Image?

An image is a read-only blueprint: a packaged filesystem containing an OS layer, a runtime, your dependencies, your code, and the
command to start it.

Docker — Complete Guide Page 10 of 38


IMAGE: second-brain-backend
┌─────────────────────────┐
│ CMD: uvicorn [Link] │ ← the start command
├─────────────────────────┤
│ Your code │
├─────────────────────────┤
│ FastAPI, SQLAlchemy, │
│ psycopg2, pydantic │
├─────────────────────────┤
│ Python 3.12 │
├─────────────────────────┤
│ Debian slim (minimal │
│ Linux filesystem) │
└─────────────────────────┘

Nothing is running. It is only stored on disk.

Analogy: an image is [Link] . You have downloaded it. It is not installed, it is not running — it is just sitting there.

Two properties to remember:

Immutable — an image never changes. To change something, you build a new image.
Layered — it is built from stacked read-only layers (more on this in Part 4).

3.4 What Is a Container?

A container is a running instance of an image.

Image ──docker run──▶ Container

Analogy: the Word installer is the image; Microsoft Word open on your screen is the container.

Word Installer → Install & launch → Word running


(Image) (Container)

For your project:

IMAGE CONTAINER
Python 3.12 A live FastAPI server
FastAPI ──run──▶ listening on port 8000
Your code serving real requests
Start command

3.5 One Image, Many Containers

second-brain-backend (one image)



┌──────────┬──────┴─────┬───────────┐
▼ ▼ ▼ ▼
Container1 Container2 Container3 Container4
port 8001 port 8002 port 8003 port 8004

Exactly like:

student1 = Student()
student2 = Student()
student3 = Student()

This is the foundation of scaling. When traffic grows, you do not rewrite anything — you run more containers from the same image behind
a load balancer. Kubernetes automates precisely this.

3.6 Image vs Container — the Comparison Table

Docker — Complete Guide Page 11 of 38


Image Container

What it is A blueprint / template A running instance

State Static, read-only Live, has a writable layer

Analogy Class, [Link] , recipe Object, running program, cooked dish

Created by docker build / docker pull docker run

Listed by docker images docker ps

Can there be many? One image... ...many containers

Lifetime Persists until deleted Starts, runs, stops, is removed

3.7 Docker Hub

When Docker printed Pulling from library/hello-world , it downloaded from Docker Hub.

GitHub stores → source code repositories


Docker Hub stores → Docker images

Docker Hub is a registry: a server that hosts images. It holds official images for python , postgres , redis , node , nginx , and millions of
community images — plus, soon, yours (Part 8).

docker pull python:3.12-slim # download without running


docker search redis # search from the terminal

Reading an image name:

postgres : 18
│ │
│ └── tag (the version)
└────────── repository name

vikaskumar8048 / second-brain-backend : v1
│ │ │
username image name tag

⚠ Never use :latest in production. It is not "the newest" in any guaranteed sense — it is just the default tag name. If it changes
under you, your deployment silently changes too. Pin explicit versions: postgres:18 , python:3.12-slim , your-app:v1.2.0 .

3.8 Inspecting Your System — The Three Daily Commands

docker images # every image stored locally

REPOSITORY TAG IMAGE ID SIZE


second-brain-backend latest a3f5c91d8e2b 212MB
hello-world latest 9c7a54a9a43c 13.3kB

docker ps # RUNNING containers only

CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES


(empty)

Why is it empty after hello-world ? Because:

Started → printed the message → finished → EXITED

A container lives exactly as long as its main process. When the process ends, the container stops. This is the most common source of "my
container keeps exiting" confusion.

docker ps -a # ALL containers: running AND stopped

Docker — Complete Guide Page 12 of 38


CONTAINER ID IMAGE STATUS NAMES
b7f2e91c4a83 hello-world Exited (0) 2 minutes ago nice_hopper

There it is. The container still exists — stopped, not deleted.

Command Shows

docker images Images (blueprints)

docker ps Running containers

docker ps -a Every container, running or stopped

3.9 The Container Lifecycle

created ──start──▶ RUNNING ──stop──▶ stopped ──rm──▶ gone


│ ▲ │
pause│ └──start──▶ RUNNING again
▼ │
paused

docker run <image> # create + start


docker stop <id> # graceful stop (SIGTERM, then SIGKILL)
docker start <id> # restart a stopped container
docker restart <id>
docker rm <id> # delete a stopped container
docker rm -f <id> # force: stop and delete
docker logs <id> # see its output
docker logs -f <id> # follow live, like tail -f
docker exec -it <id> bash # open a shell INSIDE the running container

docker exec -it <id> bash is your most valuable debugging tool. It puts you inside the container so you can check whether your
files are really there, whether the env vars are set, and whether the app can reach the database.

3.10 The One Beautiful Diagram

Docker Hub

docker pull


IMAGE

docker run


CONTAINER


Running Application

This is Docker. Everything else builds on it.

3.11 Exercise

docker images
docker ps
docker ps -a

Observe the difference between an image, a running container, and a stopped container. If you can explain those three outputs, you
have the core concept.

3.12 Interview Questions

Q1. Difference between an image and a container? An image is a read-only blueprint containing the application, its dependencies, and
its start command. A container is a running instance of that image. One image can produce many containers — exactly like a class and its
objects.

Docker — Complete Guide Page 13 of 38


Q2. What is Docker Hub? A public registry that stores and distributes Docker images, analogous to what GitHub is for source code.

Q3. Why did docker ps show nothing after running hello-world? Because a container only lives as long as its main process. hello-
world printed its message and exited, so the container stopped. docker ps -a still shows it.

Q4. Can one image run multiple containers? Yes — that is how horizontal scaling works. Multiple identical containers run from one
image behind a load balancer.

Q5. Are containers stateless? The container's writable layer is deleted with the container, so anything written inside is lost. Durable
state must go into a volume or an external database. That is why containers are treated as disposable.

Docker — Complete Guide Page 14 of 38


Part 4 — Dockerfile

4.1 Why It Is Needed

You tell Docker: "Run my FastAPI project."

Docker asks: "How?"

It does not know:

Which base OS?


Which Python version?
Which packages?
Which file starts the server?
Which port?

Someone has to answer. That someone is the Dockerfile.

4.2 The Recipe Analogy

Making Maggi:

Take water → boil → add noodles → add masala → cook 2 minutes

If everyone follows the same recipe, everyone gets the same Maggi.

A Dockerfile is a recipe for building an image. Same recipe → same image → same behaviour, on every machine.

4.3 The Architecture

Dockerfile ← the recipe (a text file)



docker build


Image ← the packaged result

docker run


Container ← the running application

A Dockerfile never runs. It only builds. This confuses beginners constantly — write it on a sticky note.

4.4 The FastAPI Dockerfile

cd ~/Downloads/second-brain/backend
touch Dockerfile

Docker — Complete Guide Page 15 of 38


# 1. Base image
FROM python:3.12-slim

# 2. Working directory inside the image


WORKDIR /app

# 3. Copy ONLY requirements first (layer caching — see 4.6)


COPY [Link] .

# 4. Install dependencies at BUILD time


RUN pip install --no-cache-dir -r [Link]

# 5. Copy the rest of the project


COPY . .

# 6. Document the port the app listens on


EXPOSE 8000

# 7. The command that runs when the container starts


CMD ["uvicorn", "[Link]:app", "--host", "[Link]", "--port", "8000"]

4.5 Every Instruction Explained

FROM python:3.12-slim

Start from an existing image that already has Linux + Python 3.12 installed.

Instead of: Ubuntu → install Python → configure


Docker gives you that, prebuilt.

Choosing a base image matters:

Base Size When to use

python:3.12 ~1 GB Needs many build tools

python:3.12-slim ~150 MB The sensible default

python:3.12-alpine ~50 MB Smallest, but uses musl libc — some Python wheels fail to build

Every Dockerfile starts with FROM . ( FROM scratch means a completely empty base.)

WORKDIR /app

Equivalent to cd /app , and it creates the directory if missing. Every instruction after this runs inside /app .

COPY [Link] .

Copies a file from your laptop into the image. The . is the destination — /app , because of WORKDIR .

RUN pip install --no-cache-dir -r [Link]

RUN executes a command while the image is being built, and the result is baked permanently into a layer.

★ RUN vs CMD — the classic interview question. RUN happens at build time (installing packages). CMD happens at run time
(starting the server). RUN pip install is done once and stored; CMD uvicorn executes each time a container starts.

--no-cache-dir tells pip not to keep its download cache, which would otherwise add ~50 MB of dead weight to the image.

COPY . .

Copy everything from the build context into /app . Comes after the install step, deliberately — see caching below.

EXPOSE 8000

Documentation only. It declares "this application listens on 8000". It does not publish the port to your laptop; that happens with -p at
run time.

CMD ["uvicorn", "[Link]:app", "--host", "[Link]", "--port", "8000"]

The command executed when the container starts. Equivalent to typing:

Docker — Complete Guide Page 16 of 38


uvicorn [Link]:app --host [Link] --port 8000

⚠ --host [Link] is mandatory, and everyone gets this wrong once. [Link] means "only accept connections from inside this
machine" — and inside a container, that means only from inside the container. Your laptop's browser would never reach it. [Link]
means "accept on all interfaces". If your containerized API is unreachable, check this first.

Also use the JSON array form ( ["uvicorn", "[Link]:app"] ), not the shell string form. The array form runs your process as PID 1
directly, so docker stop delivers the shutdown signal to it properly.

CMD vs ENTRYPOINT

CMD ENTRYPOINT

Purpose The default command The fixed executable

Overridden by docker run <image> other-cmd ? ✔ Yes, easily ✘ No — arguments are appended instead

Use CMD for normal applications. Use ENTRYPOINT when the image is one specific tool.

4.6 Layers and Build Cache ★★

Every instruction creates a layer, and Docker caches each one.

Layer 5: CMD uvicorn ...


Layer 4: COPY . . ← changes on EVERY code edit
Layer 3: RUN pip install ... ← slow (30-120s)
Layer 2: COPY [Link] .
Layer 1: FROM python:3.12-slim

The rule: when a layer changes, that layer and every layer above it must be rebuilt. Cached layers below are reused instantly.

Now the reason for the odd ordering becomes obvious:

✔ CORRECT (what we wrote) ✘ WRONG


COPY [Link] . COPY . .
RUN pip install ... RUN pip install ...
COPY . .

Edit one line of code: Edit one line of code:


pip install is CACHED COPY . . changed
rebuild takes ~2 seconds → pip install reruns
→ rebuild takes ~90 seconds

Golden rule of Dockerfiles: put the things that change least at the top, and the things that change most at the bottom.
Your code changes fifty times a day; your dependencies change once a week.

This is a genuinely strong interview answer — most candidates can write a Dockerfile, few can explain why the lines are in that order.

4.7 .dockerignore ★

Just like .gitignore . Create backend/.dockerignore :

.venv/
__pycache__/
*.pyc
.git/
.env
*.md
tests/
.pytest_cache/

Three reasons this matters:

1. Speed — the entire build context is sent to the Docker daemon first; without this, you upload your 400 MB .venv on every build
2. Size — COPY . . would otherwise bake junk into the image
3. Security — it stops .env and .git (which contains your whole history) from being copied into an image you might publish publicly

Docker — Complete Guide Page 17 of 38


Never bake secrets into an image. Anyone who pulls the image can extract every layer and read them. Secrets are passed at run
time, via environment variables.

4.8 Build the Image

cd ~/Downloads/second-brain/backend
docker build -t second-brain-backend .

Piece Meaning

docker build Build an image

-t second-brain-backend Tag (name) it

. The build context — the current directory, where the Dockerfile lives

That trailing . is not decoration. It tells Docker which folder to send as the build context. Forgetting it is the most common build error.

Verify:

docker images

REPOSITORY TAG IMAGE ID SIZE


second-brain-backend latest a3f5c91d8e2b 212MB
hello-world latest 9c7a54a9a43c 13.3kB

Useful variants:

docker build -t second-brain-backend:v1 . # explicit tag


docker build --no-cache -t app . # ignore the cache entirely
docker history second-brain-backend # see the layers and their sizes

4.9 Interview Questions

Q1. What is a Dockerfile? A text file of instructions that Docker follows to build an image — the base image, dependencies, files to copy,
and the start command.

Q2. RUN vs CMD ? RUN executes during the build and its result is stored in a layer; CMD defines the command executed when a container
starts from the image.

Q3. CMD vs ENTRYPOINT ? CMD provides a default command that is easily overridden at docker run ; ENTRYPOINT fixes the executable, and
any run arguments are appended to it.

Q4. What does EXPOSE do? It documents which port the application listens on. It does not publish anything — actual publishing requires -
p host:container at run time.

Q5. Why copy [Link] before the rest of the code? To exploit layer caching. Dependencies change rarely; code changes
constantly. This ordering means a code edit does not invalidate the expensive pip install layer.

Q6. How would you reduce image size? Use a slim base image, add a .dockerignore , combine RUN commands, use --no-cache-dir
with pip, and use a multi-stage build to leave build tools out of the final image.

Q7. What is the build context? The directory sent to the Docker daemon when building — the . at the end of docker build . Everything
in it (minus .dockerignore entries) is uploaded, which is why the ignore file matters for build speed.

Docker — Complete Guide Page 18 of 38


Part 5 — Containerizing FastAPI

5.1 Run Your Own Image

docker run -p 8000:8000 second-brain-backend

Open [Link] — your Swagger UI, served from inside a container.

5.2 Port Mapping Explained ★

-p 8000:8000
│ │
│ └── port INSIDE the container
└─────── port on YOUR laptop

Your browser
localhost:8000


┌───────────────────┐
│ Your laptop │
│ port 8000 │
└───────────────────┘
│ Docker forwards

┌───────────────────┐
│ Container │
│ port 8000 │ ← uvicorn listening on [Link]:8000
└───────────────────┘

A container's network is isolated by default. Without -p , nothing on your laptop can reach it — the app runs perfectly and is completely
unreachable.

The two sides need not match:

docker run -p 3000:8000 second-brain-backend


# browser → localhost:3000 → container:8000

This is how you run three copies at once:

docker run -p 8001:8000 second-brain-backend


docker run -p 8002:8000 second-brain-backend
docker run -p 8003:8000 second-brain-backend

5.3 The Flags You Will Actually Use

docker run \
-d \ # detached — run in the background
--name backend \ # a readable name instead of "nice_hopper"
-p 8000:8000 \ # publish the port
-e DATABASE_URL=postgresql://... \ # pass an environment variable
--restart unless-stopped \ # restart automatically after a crash/reboot
second-brain-backend

Docker — Complete Guide Page 19 of 38


Flag Purpose

-d Background (detached). Without it your terminal is occupied

--name Name it, so you can write docker logs backend

-p Publish a port

-e Set an environment variable

--env-file .env Load many variables from a file

-v Mount a volume

--rm Delete the container automatically when it stops

--restart unless-stopped Survive crashes and reboots

5.4 The Professional Insight ★

Compare what you were doing before:

source .venv/bin/activate
uvicorn [Link]:app --reload

with what you do now:

docker run -p 8000:8000 second-brain-backend

The application is no longer using your host Python installation. It uses the Python runtime packaged inside the image. Your laptop
could have no Python at all and it would still run.

This is Docker's central benefit in one sentence: everyone runs the exact same environment.

5.5 Debugging a Container

docker logs backend # what did it print?


docker logs -f backend # follow live
docker exec -it backend bash # get a shell inside
docker inspect backend # full configuration as JSON
docker stats # live CPU and memory usage

Inside the container:

ls -la /app # is my code actually there?


env | grep DATABASE # are my env vars set?
python -c "import fastapi" # did the install work?

5.6 The Two Most Common Problems

1. "It starts and immediately exits."

Run docker logs <id> . Usually a Python traceback — a missing module, or a wrong path in [Link]:app . Remember: the container dies
with its main process.

2. "It's running but localhost:8000 refuses to connect."

Three suspects, in order:

Did you pass -p 8000:8000 ?


Is uvicorn bound to [Link] and not [Link] ?
Is the app crashing? ( docker logs )

5.7 A Production-Grade Dockerfile

Docker — Complete Guide Page 20 of 38


The one in Part 4 is correct and fine for learning. Here is what a production version adds:

FROM python:3.12-slim

# do not write .pyc files; do not buffer stdout (so logs appear immediately)
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1

WORKDIR /app

COPY [Link] .
RUN pip install --no-cache-dir -r [Link]

COPY . .

# run as a non-root user


RUN useradd --create-home appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 8000

CMD ["uvicorn", "[Link]:app", "--host", "[Link]", "--port", "8000"]

Addition Why

PYTHONUNBUFFERED=1 Without it, Python buffers output and docker logs appears empty during a crash

PYTHONDONTWRITEBYTECODE=1 No .pyc clutter in the image

USER appuser If the app is compromised, the attacker is not root inside the container. Default-root is one of the most common
container security findings

No --reload Reload is a development feature; it watches the filesystem and wastes resources in production

Docker — Complete Guide Page 21 of 38


Part 6 — Docker Compose

6.1 Why It Exists

Real applications are never one container. Yours already has:

FastAPI + PostgreSQL + [Link]

and will soon add Redis, Celery, and Nginx.

Right now:

Terminal 1 → PostgreSQL
Terminal 2 → Backend
Terminal 3 → Frontend

Three terminals. Three commands, each with a long list of flags. Three sets of configuration to keep in your head, and a specific order to
start them in.

Docker Compose says: put it all in one file, then start everything together.

docker compose up

6.2 The Orchestra Analogy

Without a conductor:

Piano Guitar Drums Violin

Everyone starts whenever they feel like it. Chaos.

Docker Compose is the conductor:

Docker Compose

┌────────────┼────────────┐
▼ ▼ ▼
Database Backend Frontend

One command. Everything starts in the right order, on a shared network, with the right configuration.

Dockerfile → builds ONE image


Docker Compose → runs MANY containers together

6.3 The Target Architecture

Docker Network (created automatically)



┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
PostgreSQL FastAPI [Link]
Container Container Container
"database" "backend" "frontend"


Named Volume (postgres_data — survives container deletion)

Notice: FastAPI will no longer connect to localhost . It connects to database — the service name. Compose gives every service a DNS
name on its private network.

6.4 The Compose File

Docker — Complete Guide Page 22 of 38


The file lives in the project root, not in backend/ :

cd ~/Downloads/second-brain
touch [Link]

services:

database:
image: postgres:18
container_name: second-brain-db
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: second_brain
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 5

backend:
build: ./backend
container_name: second-brain-backend
depends_on:
database:
condition: service_healthy
ports:
- "8000:8000"
environment:
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@database:5432/second_brain
restart: unless-stopped

volumes:
postgres_data:

And a .env file next to it (add it to .gitignore ):

POSTGRES_PASSWORD=your_strong_password

Compose reads .env from the project root automatically and substitutes ${POSTGRES_PASSWORD} . This keeps the password out of the file
you commit — the original tutorial hard-codes it, which is fine for learning and dangerous in a repository.
Note on version: — older tutorials start the file with version: "3.9" . That key is now obsolete and Compose will warn you about it.
Modern Compose files simply begin with services: .

6.5 Every Key Explained

services:

"Which containers should Docker create?" Each key underneath ( database , backend ) becomes one container and one hostname.

image: vs build: ★

database:
image: postgres:18 # DOWNLOAD a ready-made image from Docker Hub

backend:
build: ./backend # BUILD from ./backend/Dockerfile — YOUR image

image: build:

Source Docker Hub Your Dockerfile

Use for PostgreSQL, Redis, Nginx Your own applications

Why Why write a Dockerfile for Postgres? Experts already did

Rule: one Dockerfile = one application. Your backend and frontend each get one. Redis and Postgres need none — they already
exist on Docker Hub.

environment:

Docker — Complete Guide Page 23 of 38


The .env of the container. Here you pass POSTGRES_USER , POSTGRES_PASSWORD , and DATABASE_URL .

The official postgres image reads POSTGRES_USER , POSTGRES_PASSWORD , and POSTGRES_DB on first start and creates that user and database
for you. No manual CREATE DATABASE — that is why the Postgres setup you did by hand in Phase 2 is now one line of YAML.

The Most Important Line ★★★

DATABASE_URL: postgresql://postgres:...@database:5432/second_brain

NOT localhost

Why? Because inside a container, localhost means that container itself. The backend container asking for localhost:5432 is asking itself
for a database it does not have.

Compose creates a private network and registers each service name as a DNS hostname:

backend container → resolves "database" → the PostgreSQL container's IP

This is one of Compose's best features, and one of its most-asked interview questions.

WITHOUT DOCKER WITH DOCKER COMPOSE


Backend Backend Container
↓ ↓
localhost:5432 database:5432
↓ ↓
PostgreSQL PostgreSQL Container

depends_on:

Start order — PostgreSQL first, then the backend.

⚠ A crucial subtlety interviewers probe: plain depends_on waits for the container to start, not for PostgreSQL to be ready to accept
connections. Postgres takes a few seconds to initialise, so your backend can start, fail to connect, and exit. The fix is the healthcheck
plus condition: service_healthy shown above — or retry logic in your application.

ports:

ports:
- "8000:8000" # laptop:container

Same meaning as -p . Note that ports is only needed for access from your laptop. Containers talk to each other over the internal
network regardless. In production you would often remove 5432:5432 so the database is not reachable from outside at all.

volumes:

volumes:
- postgres_data:/var/lib/postgresql/data

Without a volume: delete the container → ALL DATA GONE


With a volume: delete the container → data still there

/var/lib/postgresql/data is where PostgreSQL stores its files inside the container. Mapping it to a named volume moves the real storage
outside the container's lifecycle.

The bottom-level volumes: block declares the named volume so Docker creates and manages it.

restart:

restart: unless-stopped

The container comes back automatically after a crash or a machine reboot.

6.6 Running It

Docker — Complete Guide Page 24 of 38


docker compose up --build

Compose performs, in order:

Build the backend image from ./backend/Dockerfile



Pull postgres:18 from Docker Hub

Create the network (second-brain_default)

Create the volume (second-brain_postgres_data)

Start the database container

Wait for its healthcheck to pass

Start the backend container

All automatically. Open [Link] — your API, talking to a PostgreSQL container, with no PostgreSQL installed on your
laptop.

6.7 The Compose Commands You Need

docker compose up # start (logs in the foreground)


docker compose up -d # start in the background
docker compose up --build # rebuild images first — after code changes
docker compose down # stop and remove containers + network
docker compose down -v # ALSO delete volumes ⚠ deletes your data
docker compose ps # what is running
docker compose logs # all logs
docker compose logs -f backend # follow one service
docker compose restart backend # restart one service
docker compose exec backend bash # shell inside a running service
docker compose build backend # rebuild just one service

⚠ down vs down -v . down keeps your named volumes, so your database survives. down -v deletes them. Type -v by accident and
your data is gone.

6.8 Adding the Frontend

The same pattern extends naturally:

frontend:
build: ./frontend
container_name: second-brain-frontend
depends_on:
- backend
ports:
- "3000:3000"
environment:
NEXT_PUBLIC_API_URL: [Link]

Careful with that last line. Server-side code in the frontend container would use [Link] (the internal DNS name). But
NEXT_PUBLIC_ variables run in the user's browser, which is outside the Docker network and has no idea what backend means. The
browser must use [Link] . Getting this backwards is one of the most common full-stack Docker bugs.

6.9 Interview Questions

Q1. What is Docker Compose? A tool for defining and running multi-container applications from a single YAML file, handling build order,
networking, volumes, and environment configuration with one command.

Q2. Dockerfile vs [Link]? A Dockerfile describes how to build one image. A Compose file describes how to run several
containers together, including which images they use, how they network, and what they persist.

Q3. How do containers find each other in Compose? Compose creates a private bridge network and registers each service name as a
DNS hostname, so the backend connects to database:5432 rather than localhost:5432 .

Q4. Why not localhost inside a container? Because localhost refers to the container itself, not the host machine or a sibling
container. Each container has its own network namespace.

Docker — Complete Guide Page 25 of 38


Q5. Does depends_on guarantee the database is ready? No — it only controls start order. Readiness requires a healthcheck with
condition: service_healthy , or retry logic in the application.

Q6. What is the difference between docker compose down and down -v ? down removes containers and the network but preserves
named volumes; -v also deletes the volumes and therefore the data.

Docker — Complete Guide Page 26 of 38


Part 7 — Networks & Volumes

7.1 Networks

By default Docker creates three networks:

Driver Behaviour

bridge The default — a private network on the host; containers reach each other by IP, and by name if on a user-defined bridge

host The container shares the host's network stack directly (no isolation, no port mapping)

none No networking at all

Compose automatically creates a user-defined bridge for your project, which is what enables DNS by service name.

docker network ls
docker network inspect second-brain_default

Manually, without Compose:

docker network create my-net


docker run -d --name db --network my-net postgres:18
docker run -d --name api --network my-net -p 8000:8000 second-brain-backend
# "api" can now reach "db" by hostname

This is exactly what Compose does for you — which is a good way to explain Compose in an interview: it is a declarative wrapper over
the docker run , docker network , and docker volume commands you would otherwise type by hand.

7.2 Volumes — The Persistence Problem

A container's filesystem is ephemeral. Everything written inside its writable layer disappears when the container is removed.

1000 notes saved in PostgreSQL



docker rm the container

WITHOUT a volume: everything lost
WITH a volume: data still there

The Three Mount Types

# 1. NAMED VOLUME — Docker manages the storage. Best for databases.


-v postgres_data:/var/lib/postgresql/data

# 2. BIND MOUNT — maps a folder on your laptop. Best for live-reloading code in dev.
-v ./backend:/app

# 3. tmpfs — in memory only, never touches disk. For secrets/scratch data.


--tmpfs /tmp

Named volume Bind mount

Location Managed by Docker ( /var/lib/docker/volumes/ ) A path you choose on the host

Portable ✔ Yes ✘ Depends on the host's directory layout

Best for Database data in any environment Source code during development

Production ✔ Preferred Generally avoided

Live Code Reload in Development

Docker — Complete Guide Page 27 of 38


backend:
build: ./backend
volumes:
- ./backend:/app # your code is mounted live
command: uvicorn [Link]:app --host [Link] --port 8000 --reload

Now editing a file on your laptop instantly reloads the server inside the container — you get Docker's consistency without rebuilding the
image for every change.

Use this in a [Link] for development only. Production should use the baked-in code from the image, with no bind
mount and no --reload .

Volume Commands

docker volume ls
docker volume inspect second-brain_postgres_data
docker volume rm <name>
docker volume prune # delete all unused volumes ⚠

7.3 Cleaning Up Disk Space

Docker quietly consumes tens of gigabytes. Learn these before your disk fills up:

docker system df # what is using space?


docker container prune # remove all stopped containers
docker image prune # remove dangling images
docker image prune -a # remove all unused images
docker system prune # containers + networks + dangling images
docker system prune -a --volumes # ⚠ nuclear option — deletes volumes too

Docker — Complete Guide Page 28 of 38


Part 8 — Docker Hub: Publishing Your Images

8.1 Why Publish?

The industry workflow:

Write code

Build a Docker image

Push the image to a registry

A teammate / a server / Kubernetes

docker pull your-image

docker run

Once your image is on Docker Hub, anyone can run your entire backend with no Python, no pip install, no dependency conflicts.
Everything is already inside the image.

8.2 The Steps

Step 1 — Create an account at [Link] .

Step 2 — Log in from Ubuntu

docker login

Username: your-username
Password: <use an Access Token, not your password>
Login Succeeded

Generate an Access Token under Account Settings → Security. Same principle as GitHub's Personal Access Token: a revocable
credential instead of your real password.

Step 3 — Check your images

docker images

REPOSITORY TAG IMAGE ID


second-brain-backend latest a3f5c91d8e2b
second-brain-frontend latest 7d21b4e9c012
postgres 18 c4e7b13f5a89

Step 4 — Tag them for Docker Hub

Docker Hub requires the format <username>/<image>:<tag> :

docker tag second-brain-backend your-username/second-brain-backend:v1


docker tag second-brain-frontend your-username/second-brain-frontend:v1

docker tag does not copy anything. It adds a second name pointing at the same image id — like a symlink.

Step 5 — Push

docker push your-username/second-brain-backend:v1


docker push your-username/second-brain-frontend:v1

Docker uploads layer by layer, and skips layers the registry already has.

Step 6 — Verify on your Docker Hub profile. You will see both repositories.

Step 7 — Anyone can now run it

Docker — Complete Guide Page 29 of 38


docker pull your-username/second-brain-backend:v1
docker run -p 8000:8000 your-username/second-brain-backend:v1

8.3 Sharing the Whole Project

Your project has three services, so share two things:

1. The GitHub repository — source code and [Link]


2. The Docker Hub images — the prebuilt backend and frontend

Your teammate then runs:

git clone [Link]


cd second-brain
docker compose up

For that to pull instead of build, swap build: for image: in the Compose file:

services:
backend:
image: your-username/second-brain-backend:v1 # was: build: ./backend
frontend:
image: your-username/second-brain-frontend:v1 # was: build: ./frontend
database:
image: postgres:18

build: → compile from source on this machine (developers)


image: → download the finished artifact (servers, teammates, Kubernetes)

8.4 Tagging Strategy ★

docker tag app your-username/app:v1.2.0 # exact version — use this in production


docker tag app your-username/app:1.2 # minor line
docker tag app your-username/app:latest # convenience only
docker tag app your-username/app:a3f5c91 # the Git commit hash — best for CI/CD

Tagging with the Git commit SHA is what serious teams do. It makes every deployed image traceable back to the exact line of code
that produced it — which is exactly what you need at 3 a.m. when production breaks.

Docker — Complete Guide Page 30 of 38


Part 9 — Docker in a Real Project

9.1 The Project Layout

second-brain/

├── backend/
│ ├── Dockerfile ← builds the FastAPI image
│ ├── .dockerignore
│ ├── [Link]
│ └── app/

├── frontend/
│ ├── Dockerfile ← builds the [Link] image
│ └── ...

├── [Link] ← runs everything together
├── .env ← secrets (gitignored)
└── .gitignore

This is exactly how a real repository is structured.

9.2 The Six Rules to Remember Forever

1. One Dockerfile = one application.

backend/Dockerfile → FastAPI image


frontend/Dockerfile → [Link] image
Redis, PostgreSQL → no Dockerfile needed; official images exist

2. A Dockerfile never runs. It only builds.

Dockerfile → build → Image → run → Container

3. Compose combines applications. Each service becomes a container; Compose wires them together.

4. Networking uses service names, not localhost .

5. Volumes make data survive. Containers are disposable; volumes are not.

6. Build ≠ Run.

docker build docker run

Creates An image A container

Analogy Compiling Executing

Needed when Code or Dockerfile changed Every time you start the app

How many times Once per change As many as you like

Image
├──▶ Container 1
├──▶ Container 2
└──▶ Container 3

9.3 The Lifecycle (memorise this)

Docker — Complete Guide Page 31 of 38


Source Code


Dockerfile
│ docker build

Image
│ docker run

Container
│ docker compose

Multiple Containers
│ docker push

Registry (Docker Hub)
│ kubectl apply

Kubernetes / Production

9.4 CI/CD — Where Docker Becomes Automation

You: git push




GitHub


GitHub Actions ← this is why you learned Git first

run the tests

docker build

docker push → Docker Hub


Servers pull the new image and restart

Every push produces a new, tested, versioned image. Nobody SSHes into a server to run git pull and pip install ever again.

9.5 Why Docker Is the Bridge to Kubernetes

Kubernetes does not understand source code. It only understands images.

apiVersion: apps/v1
kind: Deployment
spec:
replicas: 5
template:
spec:
containers:
- name: backend
image: your-username/second-brain-backend:v1

Notice: there is no Python here. No [Link] , no build step. Just an image name and replicas: 5 .

Kubernetes pulls that image and runs five identical containers, restarts them if they crash, and scales them up or down. That is only
possible because the image is a complete, self-contained, immutable artifact.

Docker → packages the application


Kubernetes → runs many copies of that package, reliably, at scale

This is why "learn Docker before Kubernetes" is not optional advice.

9.6 What Your Images Let You Do Tomorrow

Run the project on any other laptop


Hand it to a teammate with one command
Deploy to AWS, Azure, GCP, or a cheap VPS
Run it in Kubernetes
Version it: v1.0 , v1.1 , v2.0

Docker — Complete Guide Page 32 of 38


Roll back instantly by deploying the previous tag

Docker — Complete Guide Page 33 of 38


Part 10 — Advanced but Interview-Worthy

You do not need these today, but knowing them separates a candidate who used Docker from one who understands it.

10.1 Multi-Stage Builds ★

The problem: compilers and build tools end up inside your final image, bloating it and widening the attack surface.

The solution: build in one stage, copy only the result into a clean second stage.

# ---------- Stage 1: build ----------


FROM python:3.12 AS builder
WORKDIR /app
COPY [Link] .
RUN pip install --user --no-cache-dir -r [Link]

# ---------- Stage 2: runtime ----------


FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["uvicorn", "[Link]:app", "--host", "[Link]", "--port", "8000"]

Only what stage 2 explicitly copies survives. Typical result for a Node or Go application: 1 GB → 50 MB.

10.2 Healthchecks

HEALTHCHECK --interval=30s --timeout=3s --retries=3 \


CMD curl -f [Link] || exit 1

Docker now reports the container as healthy or unhealthy rather than merely "running". Compose can wait on it, and orchestrators can
restart on it.

Remember the /health endpoint you built in the FastAPI guide? This is what it was for.

10.3 Resource Limits

deploy:
resources:
limits:
cpus: "1.0"
memory: 512M

Without limits, one runaway container can consume the entire host. This is cgroups , exposed as configuration.

10.4 Security Practices

Practice Why

Run as a non-root user ( USER appuser ) Limits the blast radius of a compromise

Pin exact base image versions Reproducible builds; no surprise updates

Never bake secrets into images Anyone can extract image layers

Use .dockerignore to exclude .env and .git Prevents accidental leaks

Scan images ( docker scout cves <image> ) Finds known CVEs in your dependencies

Prefer slim/distroless bases Fewer packages means fewer vulnerabilities

Docker — Complete Guide Page 34 of 38


10.5 What We Deliberately Skip For Now

✘ Docker Swarm ✘ Custom network drivers


✘ BuildKit internals ✘ Container registries beyond Docker Hub

You will learn these in context — Redis when you add caching, Kafka when you add streaming, and the rest when you deploy to AWS.
Learning tools in the situation that needs them beats memorising features in isolation.

Docker — Complete Guide Page 35 of 38


Part 11 — Cheat Sheet, Troubleshooting & Interviews

11.1 Command Cheat Sheet

# ---------- IMAGES ----------


docker build -t myapp . # build from the Dockerfile here
docker build -t myapp:v1 . # with a tag
docker images # list local images
docker pull postgres:18 # download an image
docker rmi <image-id> # delete an image
docker history myapp # inspect layers
docker tag myapp user/myapp:v1 # add a registry name
docker push user/myapp:v1 # upload to Docker Hub

# ---------- CONTAINERS ----------


docker run myapp # run in the foreground
docker run -d -p 8000:8000 --name api myapp # background + port + name
docker run -it ubuntu bash # interactive shell
docker ps # running containers
docker ps -a # all containers
docker stop api # stop
docker start api # start again
docker restart api
docker rm api # delete (must be stopped)
docker rm -f api # force delete
docker logs -f api # follow the logs
docker exec -it api bash # shell inside a running container
docker inspect api # full JSON configuration
docker stats # live resource usage

# ---------- COMPOSE ----------


docker compose up # start everything
docker compose up -d --build # rebuild and run in the background
docker compose down # stop and remove (volumes kept)
docker compose down -v # ⚠ also delete volumes
docker compose ps
docker compose logs -f backend
docker compose exec backend bash
docker compose restart backend

# ---------- CLEANUP ----------


docker system df # disk usage
docker container prune # remove stopped containers
docker image prune -a # remove unused images
docker volume ls
docker system prune -a # ⚠ aggressive cleanup

11.2 Troubleshooting Table

Docker — Complete Guide Page 36 of 38


Symptom Cause Fix

Cannot connect to the Docker Daemon not running sudo systemctl start docker
daemon

permission denied ... [Link] User not in the docker group sudo usermod -aG docker $USER && newgrp
docker

Container exits immediately The main process crashed or finished docker logs <id> and read the traceback

localhost:8000 refuses to connect Missing -p , or bound to [Link] Add -p 8000:8000 ; use --host [Link]

port is already allocated Something else uses that port Change the host port, or stop the other
process

Backend cannot reach the database Used localhost instead of the service name Use database:5432

connection refused on first Postgres not ready yet Add a healthcheck + condition:
Compose start service_healthy

Code changes do not appear The image still has the old code docker compose up --build , or bind-mount in
dev

COPY failed: file not found Path is outside the build context, or excluded by Fix the path or the ignore file
.dockerignore

Build is slow every single time Bad layer ordering Copy [Link] and install before
COPY . .

Data lost after compose down Used -v , or never had a volume Define a named volume; avoid down -v

Disk full Accumulated images and volumes docker system df then docker system prune

no space left on device during Same Prune, and use a slimmer base image
build

11.3 Explain Docker in 60 Seconds

"Docker packages an application together with its runtime, libraries, and configuration into an image — an immutable, layered
blueprint built from a Dockerfile. Running that image creates a container, which is really just a host process isolated using Linux
namespaces and cgroups, so it starts in under a second and shares the host kernel instead of shipping a whole guest OS like a VM. The
relationship is the same as a class and its objects: one image, many containers, which is how you scale horizontally. Real projects have
several services, so Docker Compose declares them in one YAML file — it builds or pulls each image, creates a private network where
services reach each other by service name rather than localhost , attaches volumes so database data survives container deletion,
and starts everything with docker compose up . Images are pushed to a registry like Docker Hub, and that image becomes the
deployment artifact: CI builds it on every push, and Kubernetes pulls and runs it, because Kubernetes understands images, not source
code. The core benefit is that the exact artifact tested on my laptop is the one running in production."

11.4 Master Interview Bank

Fundamentals

1. What is Docker and what problem does it solve?


2. Docker vs a virtual machine?
3. How are containers isolated? (namespaces, cgroups)
4. What is the Docker daemon? What is containerd?

Images & Containers

5. Image vs container?
6. Can one image run many containers?
7. What is Docker Hub / a registry?
8. What are image layers, and why does ordering matter?
9. Why should you avoid the latest tag?
10. Are containers stateless? Where does state go?

Dockerfile

Docker — Complete Guide Page 37 of 38


11. What is a Dockerfile?
12. RUN vs CMD ? CMD vs ENTRYPOINT ?

13. What does EXPOSE actually do?


14. Why copy [Link] before the source code?
15. What is .dockerignore and why does it matter?
16. How do you reduce image size? What is a multi-stage build?
17. Why run as a non-root user?

Compose & Runtime

18. What is Docker Compose and how does it differ from a Dockerfile?
19. How do containers discover each other in Compose?
20. Why can't a container use localhost to reach another container?
21. Does depends_on wait for readiness? How do you actually wait?
22. Named volume vs bind mount — when do you use each?
23. What does -p 8000:8000 mean, and which side is which?
24. Why must uvicorn bind to [Link] inside a container?

Production

25. How do secrets get into a container? (Not baked into the image.)
26. What is a healthcheck for?
27. Describe the CI/CD flow from git push to a running container.
28. Why is Docker a prerequisite for Kubernetes?

11.5 Final Checklist

☐ Docker installed from the official repository; runs without sudo


☐ Can explain image vs container using the class/object analogy
☐ Know the difference between docker images , docker ps , and docker ps -a
☐ Wrote a Dockerfile and can explain every line
☐ Understand layer caching and why [Link] is copied first
☐ Created a .dockerignore
☐ Built an image and ran it with -p 8000:8000
☐ Know why --host [Link] is required
☐ Wrote a [Link] with a database, a backend, and a volume
☐ Understand why the connection string says database and not localhost
☐ Ran the whole stack with docker compose up --build
☐ Confirmed data survives docker compose down (but not down -v )
☐ Tagged and pushed an image to Docker Hub
☐ Can debug with docker logs and docker exec -it ... bash

11.6 Where This Fits in the Roadmap

✔ Linux
✔ Git & GitHub
✔ FastAPI
✔ PostgreSQL
✔ Docker ← you are here

Redis (Dockerized — caching, sessions, rate limiting)

Kafka (Dockerized)

AWS (deploy the image)

Kubernetes (orchestrate many copies of the image)

Notice that Docker never really "ends". Every technology from here on arrives as a container — which is exactly why learning it
incrementally, in the context where it is used, beats memorising features in isolation.

Docker — Complete Guide Page 38 of 38

You might also like