# ╔══════════════════════════════════════════════════════════════════╗
# ║ DOCKER — COMPLETE NOTES WITH COMMANDS & EXPLANATIONS ║
# ║ For: EmpOS Django Project ║
# ╚══════════════════════════════════════════════════════════════════╝
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PART 1 — WHAT IS DOCKER? (Core Concepts)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Docker → Platform to build, ship & run apps in containers
Container → A running isolated environment (like a tiny VM, but
lightweight)
Image → A blueprint/snapshot to create containers (read-only)
Dockerfile → Text file with instructions to BUILD an image
Volume → Persistent storage that survives container restarts
Network → How containers talk to each other
Compose → Tool to manage multi-container apps with one YAML file
┌──────────────────────────────────────┐
│ Dockerfile → Image → Container │
│ (recipe) (cake) (eating it)│
└──────────────────────────────────────┘
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PART 2 — DOCKERFILE EXPLAINED (Line by Line)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FROM python:3.12-slim
│
└─ Start with an official Python 3.12 image (slim = smaller size)
This is the BASE layer. Everything builds on top of this.
WORKDIR /app
│
└─ Set the working directory inside the container.
All future commands run from here. Like doing: cd /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
│
└─ Environment variables:
PYTHONDONTWRITEBYTECODE → Don't create .pyc cache files (clean)
PYTHONUNBUFFERED → Show print() output immediately in logs
RUN apt-get update && apt-get install -y gcc
│
└─ RUN executes a shell command during the BUILD phase.
Installs gcc (needed to compile some Python packages like Pillow).
rm -rf /var/lib/apt/lists/* → removes cache to shrink image size.
COPY [Link] .
│
└─ COPY <source-on-your-machine> <destination-in-container>
Copies [Link] into /app/[Link]
We do this BEFORE copying all code → Docker caches this layer.
If code changes but requirements don't, pip install is SKIPPED (faster
builds).
RUN pip install --upgrade pip && pip install -r [Link]
│
└─ Installs all Python packages defined in [Link].
This runs once during build, not every time you start the container.
COPY . .
│
└─ Copies ALL files from your project folder into /app inside the container.
The first dot = current folder on your machine
The second dot = /app inside the container
CMD ["sh", "-c", "python [Link] migrate && python [Link] runserver
[Link]:8000"]
│
└─ CMD is the DEFAULT command that runs when the container STARTS.
It runs migrations first, then starts Django's dev server.
[Link]:8000 = listen on all interfaces (required so your host can reach it).
Note: Only one CMD per Dockerfile. It can be overridden at runtime.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PART 3 — DOCKER-COMPOSE EXPLAINED (Line by Line)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
version: "3.9"
└─ Compose file format version. Use 3.x for modern features.
services:
└─ Defines all your containers. Each service = one container.
web:
└─ Name of this service. Could be "app", "backend", anything.
build: .
└─ Build the image using the Dockerfile in the current directory (.)
container_name: empos_web
└─ Give the container a friendly name (instead of a random ID).
ports:
- "8000:8000"
└─ Map host port 8000 → container port 8000.
Format: "HOST_PORT:CONTAINER_PORT"
Access in browser: [Link]
volumes:
- .:/app ← bind mount: your code ↔ container /app (live reload)
- media_data:/app/media ← named volume: persists uploaded files
└─ Volumes allow data to survive container restarts.
Bind mount (.) = your local folder is shared with the container live.
Named volume = Docker manages the storage location.
environment:
- DJANGO_SETTINGS_MODULE=[Link]
- DEBUG=True
└─ Set environment variables inside the container.
These override or supplement what's in your code.
restart: unless-stopped
└─ Automatically restart the container if it crashes.
Options: no | always | on-failure | unless-stopped
volumes:
media_data:
└─ Declare the named volume so Docker creates and manages it.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PART 4 — RUNNING THIS PROJECT (Step-by-Step)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
STEP 1 — Install Docker Desktop
──────────────────────────────
Download from: [Link]
(Includes Docker Engine + Docker Compose + GUI)
STEP 2 — Navigate to the project folder
──────────────────────────────────────
cd path/to/employee_system
STEP 3 — Build & Run everything
────────────────────────────────
docker compose up --build
Explanation:
docker compose up → Start all services defined in [Link]
--build → Force rebuild the image (use after code changes)
STEP 4 — Open in browser
─────────────────────────
[Link] → Main app
[Link] → Django admin panel
STEP 5 — Create a superuser (for admin panel)
──────────────────────────────────────────────
docker compose exec web python [Link] createsuperuser
Explanation:
docker compose exec → Run a command inside a running container
web → Name of the service (from [Link])
python [Link] createsuperuser → Django command to create admin user
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PART 5 — ESSENTIAL DOCKER COMMANDS (Full Reference)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
── BUILD ───────────────────────────────────────────────────────────
docker build -t empos .
└─ Build an image from Dockerfile in current dir, tag it "empos"
-t = tag/name for the image
. = build context (where to find Dockerfile + files)
docker build -t empos:v2 .
└─ Build with a specific version tag. Format: name:tag
docker build --no-cache -t empos .
└─ Build without using cached layers (clean rebuild from scratch)
── RUN ─────────────────────────────────────────────────────────────
docker run empos
└─ Run a container from the "empos" image.
docker run -p 8000:8000 empos
└─ Run and map port 8000 (host) → 8000 (container)
-p HOST:CONTAINER
docker run -d -p 8000:8000 empos
└─ Run in detached mode (background, doesn't block your terminal)
-d = detach
docker run -d -p 8000:8000 --name my_app empos
└─ Run with a custom container name
docker run --rm -p 8000:8000 empos
└─ Automatically remove the container when it stops
--rm = remove on exit (great for testing)
docker run -v $(pwd):/app -p 8000:8000 empos
└─ Mount current directory into /app (live code sync)
-v SOURCE:DESTINATION
docker run -e DEBUG=True -e SECRET_KEY=abc empos
└─ Pass environment variables at runtime
-e KEY=VALUE
── DOCKER COMPOSE ──────────────────────────────────────────────────
docker compose up
└─ Start all services. Reads [Link] automatically.
docker compose up --build
└─ Start AND rebuild images first. Use after Dockerfile changes.
docker compose up -d
└─ Start in detached mode (background).
docker compose down
└─ Stop and remove all containers, networks created by compose.
docker compose down -v
└─ Also remove named volumes ( deletes all saved data/uploads!)
docker compose restart
└─ Restart all services.
docker compose restart web
└─ Restart only the "web" service.
docker compose logs
└─ Show logs from all services.
docker compose logs web
└─ Show logs from the "web" service only.
docker compose logs -f web
└─ Follow/stream logs in real time (-f = follow)
docker compose exec web bash
└─ Open a bash shell inside the running "web" container.
Use this to debug, run commands, check files inside the container.
docker compose exec web python [Link] migrate
└─ Run Django migrations inside the container.
docker compose exec web python [Link] createsuperuser
└─ Create Django admin user inside the container.
docker compose exec web python [Link] shell
└─ Open Django Python shell inside the container.
docker compose ps
└─ List all running containers for this compose project.
docker compose build
└─ Build/rebuild images WITHOUT starting them.
── CONTAINERS ──────────────────────────────────────────────────────
docker ps
└─ List all RUNNING containers.
docker ps -a
└─ List ALL containers (including stopped ones).
-a = all
docker stop empos_web
└─ Gracefully stop a running container (sends SIGTERM, waits).
docker stop $(docker ps -q)
└─ Stop ALL running containers at once.
docker start empos_web
└─ Start a stopped container.
docker restart empos_web
└─ Restart a container.
docker rm empos_web
└─ Remove (delete) a stopped container.
docker rm -f empos_web
└─ Force remove a running container without stopping first.
-f = force
docker exec -it empos_web bash
└─ Open interactive bash terminal inside a running container.
-it = interactive + tty (lets you type)
docker exec empos_web python [Link] migrate
└─ Run a single command inside a running container (non-interactive).
docker logs empos_web
└─ Show logs from a container.
docker logs -f empos_web
└─ Stream/follow logs live.
docker logs --tail 50 empos_web
└─ Show only the last 50 lines of logs.
docker inspect empos_web
└─ Show detailed JSON info about a container (IP, mounts, config).
docker stats
└─ Live CPU, memory, network usage of all running containers.
── IMAGES ──────────────────────────────────────────────────────────
docker images
└─ List all images on your machine.
docker pull python:3.12-slim
└─ Download an image from Docker Hub.
docker rmi empos
└─ Remove an image from your machine.
docker rmi $(docker images -q)
└─ Remove ALL images (careful!).
docker image prune
└─ Remove all dangling (unused, untagged) images.
docker tag empos myusername/empos:latest
└─ Tag an image for pushing to Docker Hub.
docker push myusername/empos:latest
└─ Push image to Docker Hub (must be logged in first).
docker login
└─ Log in to Docker Hub from the terminal.
── VOLUMES ─────────────────────────────────────────────────────────
docker volume ls
└─ List all volumes.
docker volume create my_volume
└─ Create a named volume manually.
docker volume rm my_volume
└─ Remove a volume.
docker volume prune
└─ Remove all unused volumes.
docker volume inspect my_volume
└─ Show details about a volume (where it's stored on host, etc.)
── NETWORKS ────────────────────────────────────────────────────────
docker network ls
└─ List all Docker networks.
docker network inspect bridge
└─ Inspect a network (see which containers are connected).
── SYSTEM CLEANUP ──────────────────────────────────────────────────
docker system prune
└─ Remove all stopped containers, unused images, networks, cache.
Safe to run but removes a lot. Add -a to also remove unused images.
docker system prune -a --volumes
└─ FULL cleanup: containers + images + volumes + networks.
Use with caution. You'll need to rebuild everything.
docker system df
└─ Show disk usage by Docker (images, containers, volumes, cache).
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PART 6 — HOW DOCKER LAYER CACHING WORKS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Every instruction in a Dockerfile creates a LAYER.
Docker caches each layer. If nothing changed, it reuses the cache.
GOOD ORDER (fast builds):
FROM python:3.12-slim ← cached (never changes)
COPY [Link] . ← cached unless requirements change
RUN pip install ... ← cached unless requirements change
COPY . . ← invalidated on every code change
CMD ... ← always re-run
BAD ORDER (slow builds):
COPY . . ← changes every time
RUN pip install ... ← re-runs every time! Wastes minutes.
Rule: Put things that change LESS OFTEN near the top.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PART 7 — COMMON ERRORS & FIXES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Error: port is already allocated
Fix: Another app is using port 8000. Change to: "8001:8000"
Or stop the conflicting app: lsof -i :8000
Error: Cannot connect to the Docker daemon
Fix: Docker Desktop is not running. Start it first.
Error: ModuleNotFoundError in container
Fix: Add the module to [Link], then: docker compose up --build
Error: [Link] (no such table)
Fix: Migrations didn't run. Run:
docker compose exec web python [Link] migrate
Changes to code not reflected inside container
Fix: Check that you have a volume bind mount in [Link]:
volumes:
- .:/app
If not using volumes, rebuild: docker compose up --build
Static files not loading
Fix: Run: docker compose exec web python [Link] collectstatic
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PART 8 — QUICK REFERENCE CHEATSHEET
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TASK COMMAND
─────────────────────────────────────────────────────────────────
Start the project docker compose up --build
Start in background docker compose up -d
Stop everything docker compose down
View running containers docker ps
View logs docker compose logs -f web
Open shell inside container docker compose exec web bash
Run Django migrations docker compose exec web python [Link]
migrate
Create admin user docker compose exec web python [Link]
createsuperuser
Rebuild after code changes docker compose up --build
Remove all containers+volumes docker compose down -v
Clean up disk space docker system prune -a
─────────────────────────────────────────────────────────────────
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PART 9 — NEXT STEPS AFTER MASTERING THIS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Use PostgreSQL instead of SQLite
→ Add a `db` service in [Link] using postgres image
→ Connect Django to it via DATABASE_URL environment variable
2. Add Nginx as a reverse proxy
→ Add an `nginx` service that forwards traffic to your Django app
→ Serve static/media files through Nginx (faster)
3. Use .env files for secrets
→ Create a .env file: SECRET_KEY=abc DEBUG=False
→ Reference in compose: env_file: - .env
→ Never commit .env to Git!
4. Multi-stage builds
→ Separate build stage (with dev tools) from runtime stage (lean)
→ Reduces final image size significantly
5. Deploy to cloud
→ Push image to Docker Hub or AWS ECR
→ Run on: AWS ECS, Google Cloud Run, DigitalOcean App Platform
→ Or orchestrate with Kubernetes (K8s)