Skip to content

Docker Essentials

Package code + its entire environment into a reproducible image, run it anywhere as a container. For data science: the same training code runs on your laptop, a CI runner, and a GPU box with zero "works on my machine."

Table of Contents

Installation / Setup

# Docker Engine (Linux) or Docker Desktop (macOS/Windows — WSL2 backend)
docker --version
docker info                     # engine healthy?
docker run hello-world          # sanity check

Mental model: Image = template (read-only layers). Container = a running instance of an image (writable layer on top). Build images once; run many containers.

Core Syntax

Images

Command Does
docker images list local images
docker pull python:3.12-slim download an image
docker build -t myimage . build from a Dockerfile
docker tag myimage repo/myimage:v1 tag an image
docker rmi myimage remove an image
docker history myimage show layers

Containers

Command Does
docker run myimage run (foreground)
docker run -d myimage run detached (background)
docker run --name train myimage name it
docker ps / docker ps -a running / all containers
docker exec -it <c> bash shell inside a running container
docker logs <c> / -f container logs (follow)
docker stop <c> / docker start <c> stop / restart
docker rm <c> delete a stopped container
docker inspect <c> full metadata

Run flags that matter for data science

docker run --rm -it \
  -p 8888:8888 \                          # ports: host:container
  -v "$PWD":/workspace \                  # volumes: host dir -> container path
  -e CUDA_VISIBLE_DEVICES=0 \             # environment variables
  -w /workspace \                         # working directory inside
  --gpus all \                            # GPU access (nvidia-container-toolkit)
  --shm-size=8g \                         # /dev/shm (PyTorch DataLoader!)
  myimage python train.py --lr 0.001

Dockerfile

FROM python:3.12-slim                 # base image
WORKDIR /app                          # set working dir
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

# Install deps FIRST for layer caching
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Then the code (changes often -> last layer)
COPY . .
CMD ["python", "train.py"]            # default command

Build with docker build -t myimage . — each instruction becomes a layer; unchanged layers are cached.

Common Operations

Logs, exec, cleanup

docker logs -f --tail 100 train          # follow training output
docker exec -it train python -c "import torch; print(torch.__version__)"
docker stats                              # live CPU/RAM of containers
docker top train                          # processes inside

Volumes & data

# Named volume (managed by Docker, survives container removal)
docker volume create mydata
docker run -v mydata:/data myimage

# Bind mount (host folder <-> container) — dev workflow
docker run -v "$PWD":/workspace myimage

# Copy files without volumes
docker cp results.csv train:/app/results.csv
docker cp train:/app/checkpoint.pt ./checkpoint.pt
docker volume ls && docker volume prune
Volume type Lives where Use for
Named volume Docker-managed persistent DB/artifacts
Bind mount your host path live code, datasets
tmpfs RAM scratch only

Networks & ports

docker network create mynet
docker run --network mynet --name api myimage
docker run --network mynet --name db postgres:16
# containers reach each other by name: api -> db:5432

docker port <c>           # which host port maps where
docker network ls

Port publishing -p 8888:8888: host 8888 → container 8888. Omit -p and the container's port is not reachable from the host.

Registries (sharing images)

docker login                     # Docker Hub / GHCR / ECR ...
docker tag myimage ghcr.io/you/myimage:v1
docker push ghcr.io/you/myimage:v1
docker pull ghcr.io/you/myimage:v1

For private registries set credentials via docker login or CI secrets — never bake passwords into an image.

Practical Examples

1. Jupyter with GPU in one command

docker run --rm -d --gpus all \
  -p 8888:8888 -v "$PWD":/home/jovyan/work \
  -e JUPYTER_TOKEN=secret \
  jupyter/datascience-notebook:latest
# open http://localhost:8888 (token: secret)

2. ML training image (multi-stage keeps it slim)

FROM python:3.12-slim AS base
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY src/ ./src/
CMD ["python", "-m", "src.train"]
docker build -t train-img .
docker run --rm --gpus all -v "$PWD/artifacts":/app/artifacts train-img

3. Two containers that talk (model + API) — see docker-compose.md for the sane way

docker network create appnet
docker run -d --network appnet --name api -p 8000:8000 myapi:latest
docker run --rm --network appnet -e API_URL=http://api:8000 myclient:latest

Common Patterns

# Interactive debugging of a build/runtime problem
docker run --rm -it --entrypoint bash myimage
docker run --rm -it myimage python

# Ephemeral throwaway run
docker run --rm -v "$PWD":/w -w /w python:3.12 python script.py

# Rebuild + rerun loop during development
docker build -t myimage . && docker run --rm -v "$PWD":/workspace myimage

# Cleanup (careful — see cleanup)
docker container prune -f        # stopped containers
docker image prune -f            # dangling images
docker system df                 # what's taking space

Best Practices

  • Order Dockerfile for caching: requirements first, code last; use --no-cache-dir.
  • Prefer slim images (python:3.12-slim) and multi-stage builds — smaller attack surface, faster pulls.
  • Never store secrets in images — use -e, env files, or secrets mounts.
  • Run as a non-root user in production images (USER 1000).
  • One process per container; manage several with Docker Compose.
  • Always mount data (volumes) rather than COPYing large datasets into images.
  • Pin base-image tags (python:3.12-slim, not latest) for reproducibility.
  • Set --shm-size for PyTorch multi-worker DataLoaders.
  • Tag with a version (:v1) or commit SHA for deployment; use .dockerignore (.git, data/, .venv/).

Common Mistakes

Mistake Consequence Fix
COPY . . before installing deps Every code change rebuilds pip install Order: deps → code
No .dockerignore .venv, data, .git copied in → huge images Add .dockerignore
Forgetting -p Can't reach the service Publish the port
Writing to the container FS Data lost on rm Mount a volume
latest tags everywhere Non-reproducible deploys Pin versions
Running as root Security risk USER non-root
PyTorch DataLoader hangs/crashes /dev/shm too small --shm-size=8g
docker run without --rm accumulation Disk fills with stopped containers --rm + periodic prune

Troubleshooting

Symptom Cause Fix
Cannot connect to the Docker daemon Engine not running / perms Start Docker Desktop/daemon; sudo usermod -aG docker $USER + logout
no space left on device Full image cache docker system prune -af
CUDA error: no kernel image available Image built for wrong CUDA Match base image to driver; rebuild
Container exits immediately App crashed or no foreground process docker logs <c>; run -it to see
port is already allocated Two containers, one port Change -p or stop the other
Can't reach service in another container Wrong network Same docker network; use container name, not localhost
permission denied on mounted files Host file perms vs container user chmod on host; run with matching --user
Image build slow every time Cache busted by early COPY Reorder; check .dockerignore

Official Documentation

Last Updated

2026-09-06 — Initial version.