Docker β Containers, Compose & Kubernetes Essentials
Last reviewed: 2026-09-17
Purpose: Practical Docker reference β image and container lifecycle, Dockerfiles, Docker Compose, networking, volumes, log rotation and cleanup, with a short introduction to Kubernetes for orchestration.
1. Containers, Images & Registries
A container packages an application with its libraries, runtime, and configuration into one immutable artefact and runs it as an isolated process on a shared kernel β fast to start, reproducible, and consistent from laptop to production.
| Concept | What it is |
|---|---|
| Image | Read-only layered filesystem plus metadata (entrypoint, env, ports) |
| Container | A running or stopped instance of an image, with a thin writable layer |
| Registry | Image distribution service (Docker Hub, GHCR, ECR, Harbor) |
| Volume / bind mount | Persistent storage: Docker-managed, or a host directory mounted in |
| Network | Virtual segment giving containers DNS names and isolated connectivity |
| Daemon | dockerd β builds, runs and manages containers, images and networks |
2. Installation (Ubuntu / Debian)
# Repository setup: follow https://docs.docker.com/engine/install/ubuntu/
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER # re-login to apply
docker run --rm hello-world # smoke test
Install from Docker's own repository rather than the distribution package, which is usually stale.
3. Images and Containers
docker pull nginx:1.27-alpine # fetch
docker images # local images; docker history shows layers
docker tag myapp:latest registry.example.com/team/myapp:1.0.0
docker push registry.example.com/team/myapp:1.0.0
docker image prune # dangling images; -a for all unused
docker run -d --name web -p 8080:80 -e APP_ENV=production \
--restart unless-stopped nginx:1.27-alpine
docker ps ; docker ps -a # running / all containers
docker logs -f web # follow stdout and stderr
docker exec -it web sh # shell inside a running container
docker stats # live CPU, memory, network
docker stop web ; docker rm web # SIGTERM, then remove
docker run --rm -it alpine sh # throwaway container, removed on exit
| Flag | Effect |
|---|---|
-d / -it |
Detached, or interactive TTY |
-p host:container |
Publish a port (omit host part for a random port) |
-e KEY=VALUE |
Inject an environment variable β never bake secrets into images |
--restart |
no, on-failure, always, unless-stopped |
-v / --mount |
Volume or bind mount; --memory/--cpus limit resources |
Use explicit tags (myapp:1.4.2) or digests in production β latest is re-pulled unpredictably and makes rollbacks hard to reason about.
4. Dockerfile
Each instruction creates a layer, so ordering from least to most frequently changed maximises build-cache reuse.
# syntax=docker/dockerfile:1
FROM python:3.12-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV PYTHONUNBUFFERED=1 PORT=8000
EXPOSE 8000
RUN useradd --create-home appuser
USER appuser
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -fsS http://localhost:8000/health || exit 1
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Multi-stage builds keep the runtime image small by discarding compilers and build tooling:
FROM golang:1.22 AS builder
RUN CGO_ENABLED=0 go build -o /out/app ./cmd/app
FROM gcr.io/distroless/static
COPY --from=builder /out/app /app
Key instructions: FROM, WORKDIR, COPY/ADD, RUN, ENV, ARG, EXPOSE, USER, HEALTHCHECK, ENTRYPOINT (fixed executable), CMD (default arguments, overridable at run time). Keep a .dockerignore beside the Dockerfile; build with docker build -t myapp:1.0.0 .
5. Docker Compose
Compose describes a multi-service application in one YAML file and manages the services as a unit β the standard way to run databases, queues and dashboards alongside an application.
# compose.yaml
services:
api:
build: .
ports:
- "8000:8000"
environment:
DATABASE_URL: postgres://app:secret@db:5432/app
depends_on: [db]
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
docker compose up -d # start the stack
docker compose ps ; docker compose logs -f api
docker compose exec db psql -U app -d app
docker compose down # stop, remove containers and networks
docker compose down -v # also delete named volumes (data loss)
Compose creates a network per project, so services reach each other by service name (db). Keep secrets in an .env file excluded from version control, or in Docker secrets β not inline in the YAML.
6. Networking
The default networks are bridge (standard for standalone containers), host (shares the host stack, port flags ignored) and none (no connectivity).
docker network create app-net
docker network ls ; docker network inspect app-net
docker network connect app-net web # attach a running container
docker exec -it api ping -c1 db # DNS on a user-defined network
| Driver | Use case |
|---|---|
| bridge | Default isolated network with built-in DNS β the normal choice |
| host | Maximum performance, no isolation |
| overlay | Multi-host networking across a cluster |
| macvlan | Container gets its own MAC/IP on the physical LAN |
| none | Fully isolated network namespace |
7. Volumes & Persistence
docker volume create pgdata ; docker volume ls ; docker volume inspect pgdata
docker volume prune # unreferenced volumes
# Named volume β databases and application state
docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16
# Bind mount β configuration and local source, read-only where possible
docker run -d --name web -v /srv/site:/usr/share/nginx/html:ro nginx:1.27-alpine
docker run --rm -v pgdata:/data -v $PWD:/backup alpine tar czf /backup/pgdata.tgz -C /data .
Container filesystems are ephemeral: anything written outside a volume disappears with the container. A volume survives docker rm but not docker volume rm β back it up explicitly.
8. Logging, Log Rotation & Cleanup
Container logs are JSON files under /var/lib/docker/containers/<id>/ and grow without limit under the default driver. Cap them globally in /etc/docker/daemon.json, then sudo systemctl restart docker:
Per container use --log-opt max-size=100m --log-opt max-file=3 at docker run, or a logging.options block in Compose. Send logs off-host with the journald, gelf or fluentd driver where logging is centralised.
docker system df # reclaimable space by category
docker system prune # stopped containers, unused networks, dangling images, build cache
docker system prune -a # also unused images
docker system prune -a --volumes # also unused volumes β destructive
docker builder prune # build cache only
# Truncate a log that is already filling the disk
sudo truncate -s 0 /var/lib/docker/containers/<container-id>/<container-id>-json.log
9. Security Basics
- Run as a non-root
USERinside the image; drop capabilities with--cap-drop=ALL --cap-add=NET_BIND_SERVICEwhen possible. - Use
--read-onlyplus atmpfsfor/tmpon stateless services; never copy secrets into an image layer β inject them at runtime. - Keep base images minimal (
-slim,-alpine,distroless) and pinned to a version or digest. - Scan before pushing:
docker scout quickview myapp:1.0.0ortrivy image myapp:1.0.0.
10. Kubernetes in a Nutshell
Docker runs containers on one host; Kubernetes orchestrates them across many, adding scheduling, self-healing, rolling updates and declarative desired state.
| Object | Role |
|---|---|
| Pod | Smallest deployable unit β containers sharing network and storage |
| Deployment | Declarative replica management; rolling updates and rollbacks |
| Service | Stable virtual IP/DNS name load-balancing across Pod replicas |
| Ingress | HTTP(S) routing from outside the cluster to Services |
| ConfigMap / Secret | Configuration and credentials injected into Pods |
| PersistentVolumeClaim | Request for durable storage backed by a PersistentVolume |
| Namespace | Logical partition for grouping and isolating resources |
kubectl config get-contexts ; kubectl get nodes ; kubectl get pods -A
kubectl logs -f deploy/api ; kubectl exec -it deploy/api -- sh
kubectl apply -f deployment.yaml
kubectl rollout status deploy/api ; kubectl rollout undo deploy/api
apiVersion: apps/v1
kind: Deployment
metadata: { name: api }
spec:
replicas: 3
selector: { matchLabels: { app: api } }
template:
metadata: { labels: { app: api } }
spec:
containers:
- name: api
image: registry.example.com/team/myapp:1.0.0
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 500m, memory: 512Mi }
Compose suits a single host and local or small-scale use; Kubernetes adds multi-node scheduling, health-based reconciliation and zero-downtime rollouts at the cost of considerably more operational complexity.
11. Troubleshooting Quick Reference
| Symptom | First commands |
|---|---|
| Container exits immediately | docker logs <name>, docker inspect <name> (exit code, State.Error) |
| Port already in use | docker ps --format '{{.Names}}\t{{.Ports}}', ss -ltnp |
| Service name unresolved | confirm a shared user-defined network (docker network inspect) |