Table of Contents
Why Docker Matters
Docker is a container runtime and tooling ecosystem focused on:
- Packaging applications and dependencies into portable images
- Running them as isolated containers
- Making builds and deployments repeatable and versioned
In this chapter, “Docker” means:
- The Docker Engine: daemon and runtime (
dockerd) - The Docker CLI: user interface (
dockercommand) - The Docker image format and associated tooling (build, registry, pull/push)
Higher‑level container concepts and alternatives (LXC, Podman, etc.) are covered in other chapters; here we focus on Docker itself.
Key strengths of Docker:
- Developer workflow: easy build–run–share cycle
- Portability: same image runs on any compatible Docker host
- Isolation: processes run in containers with their own view of filesystem, network, etc.
- Reproducibility: environment declared in a Dockerfile and versioned in Git
Installing and Running Docker (Overview)
Installation details vary per distro, so here’s only the high‑level picture.
Typical components after installation:
dockerd– the Docker daemon (background service)docker– CLI that talks to the daemon (usually via Unix socket)containerdandrunc– lower-level container runtime components (used internally)
On most systems, you will:
- Install Docker Engine via your distro’s package manager or from Docker’s repo.
- Ensure the service is running:
systemctl status docker - Add your user to the
dockergroup (optional, to avoid always usingsudo). - Test with:
docker run hello-worldThis pulls a test image and runs a tiny container to verify the setup.
Core Docker Concepts
Images
An image is a read‑only template used to create containers.
- Built from a Dockerfile or pulled from a registry
- Made of a stack of layers
- Identified by:
- Repository and name:
nginx - Optional tag:
nginx:1.27-alpine - Or by immutable digest:
@sha256:...
Common commands:
docker pull nginx:latest– download image from a registrydocker images– list local imagesdocker rmi IMAGE– remove local image
Images are immutable: you never “change” an image; you create a new image/layer and tag it.
Containers
A container is a running (or stopped) instance of an image.
- Has its own:
- Process space (via namespaces)
- Filesystem view (image + writable layer)
- Network interfaces
- Lightweight: starts in milliseconds, shares host kernel
Core lifecycle:
- Create and start
- Optionally restart/stop
- Remove when no longer needed
Basic commands:
docker ps– running containersdocker ps -a– all containers (including stopped)docker run– create + start a new containerdocker stop– gracefully stopdocker rm– remove
Registries and Repositories
A registry is a service that stores and distributes images.
- Default: Docker Hub (
hub.docker.com) - Others: GitHub Container Registry, GitLab, private registries, cloud vendor registries
A repository is a named collection of related images in a registry:
- Example:
library/nginx, tags:latest,alpine,1.27, etc. - Full image name:
registry.example.com/myteam/myapp:1.0.0
Workflow:
docker pull REPO:TAG– downloaddocker push REPO:TAG– upload (requires login and permissions)
Running Containers
The `docker run` Command
docker run is the central command for starting containers:
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]Key ideas:
- If image is not local, it will be pulled automatically.
- By default, the container runs in the foreground and you see the main process output.
- When that process exits, the container stops.
Examples
Run a one‑off command in a container:
docker run --rm alpine echo "Hello from Alpine"alpine– imageecho "Hello from Alpine"– command executed inside the container--rm– automatically remove container after it exits
Run an interactive shell:
docker run -it --rm alpine sh-i– keep STDIN open-t– allocate a pseudo‑TTY- You get a shell prompt inside the container.
Detached Containers
To run in the background, use -d:
docker run -d --name web nginx-d– detached mode--name web– assign a custom container namenginx– default command runs the Nginx web server inside a container
See it running:
docker psStop and remove:
docker stop web
docker rm webPort Mapping
Containers have their own network namespace. To expose container ports to the host, use -p:
docker run -d --name web -p 8080:80 nginx-p HOST_PORT:CONTAINER_PORT- Now
http://localhost:8080reaches port80inside the container.
You can map different host ports or multiple ports:
docker run -d -p 8000:80 -p 8443:443 nginxManaging Container Lifecycle
Useful commands:
docker stop CONTAINER– send SIGTERM, then SIGKILL if neededdocker start CONTAINER– start a stopped containerdocker restart CONTAINER– stop then startdocker logs CONTAINER– show stdout/stderr of main processdocker logs -f CONTAINER– follow logsdocker exec– run extra commands inside a running container
Example: debug a running container:
docker exec -it web sh
This starts sh inside the existing web container (Nginx remains running).
Docker Volumes and Storage Basics
Containers are ephemeral by default:
- Their writable layer is deleted when the container is removed.
- Any data stored only inside the container’s filesystem is not meant to be persistent.
To persist or share data, Docker provides:
Volumes
A volume is managed by Docker and stored outside the container’s writable layer.
- Ideal for application data (databases, uploads, logs)
- Can be reused across containers
Create and use a volume:
docker volume create mydata
docker run -d \
-v mydata:/var/lib/mysql \
--name db mysql:8-v VOLUME_NAME:CONTAINER_PATH
List volumes:
docker volume ls
docker volume inspect mydataRemove unused volumes:
docker volume rm mydataBind Mounts
A bind mount maps an existing host path into the container.
Example:
docker run -d \
-v /home/user/web:/usr/share/nginx/html:ro \
-p 8080:80 \
--name web \
nginx-v HOST_PATH:CONTAINER_PATH[:OPTIONS]:ro– read‑only inside container- Edits to
/home/user/webon the host are immediately visible to Nginx.
Bind mounts are useful for:
- Local development (editing code on the host, running in container)
- Inspecting or backing up container data from the host
Basic Docker Networking
A full treatment of container networking is elsewhere; here are the essentials to use Docker comfortably.
Default Networks
Docker creates some networks automatically:
bridge– default network for containers started without--network; NAT’ed behind hosthost– containers share the host’s network namespace (no isolation)none– no network connectivity
Check networks:
docker network lsInspect a network:
docker network inspect bridgeUser-Defined Bridge Networks
User-defined bridge networks allow containers to communicate by name:
docker network create mynet
docker run -d --name db --network mynet mysql:8
docker run -d --name app --network mynet myapp:latest
Inside app, the hostname db resolves automatically to the DB container’s IP.
Advantages:
- Easier service discovery
- Isolated segment: containers on
mynetare not reachable from defaultbridgeunless connected explicitly.
Attach an existing container:
docker network connect mynet some_containerBuilding Images with Dockerfiles
A Dockerfile describes how to build an image step by step.
Common structure:
FROM ubuntu:22.04
# Install dependencies
RUN apt-get update && apt-get install -y \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy application files
COPY . /app
# Set environment variable
ENV APP_ENV=production
# Expose port (documentation only)
EXPOSE 8080
# Default command
CMD ["./start.sh"]Key instructions (high‑level overview):
FROM– base image; every Dockerfile must start with thisRUN– execute a command during build, produce a new layerCOPY/ADD– copy files into the imageWORKDIR– set working directory for subsequent instructionsENV– set environment variablesEXPOSE– documentation of ports the container listens onCMD– default command/arguments when container runsENTRYPOINT– main executable; often combined withCMD
Building an Image
From a directory containing a Dockerfile:
docker build -t myapp:1.0.0 .-t– tag name; can include a repository and tag (myrepo/myapp:latest).– build context (files available to the Dockerfile, usually the current dir)
Then run it:
docker run -d -p 8080:8080 --name myapp myapp:1.0.0Image Tagging and Versioning Basics
Tags act like labels:
myapp:latest– conventionally “most recent build”myapp:1.0.0– specific versionmyregistry.local/myteam/myapp:dev– dev build on private registry
Common practices:
- Use explicit version tags for reproducibility.
- Avoid relying on
:latestin production. - Use semantic versioning (
MAJOR.MINOR.PATCH) where appropriate.
Tagging an existing image:
docker tag myapp:1.0.0 myregistry.local/myteam/myapp:1.0.0Pushing to a registry:
docker push myregistry.local/myteam/myapp:1.0.0Inspecting and Debugging Containers
Useful tools for understanding what’s happening inside Docker.
Inspecting Containers and Images
docker inspect NAME_OR_ID– detailed JSON description (mounts, network, config)docker stats– live resource usage per containerdocker top CONTAINER– processes running inside container
Example:
docker inspect web | jq '.[0].NetworkSettings.IPAddress'Interactive Debugging
To troubleshoot a running container:
docker exec -it web /bin/sh
# or /bin/bash depending on the imageIf the container exits immediately and you want to keep it alive to inspect:
docker run -it --entrypoint sh IMAGEThis overrides the default entrypoint and gives you a shell.
Cleaning Up
Containers and images can accumulate over time.
Basic cleanup:
- Remove stopped containers:
docker container prune- Remove unused images:
docker image prune- Remove both (plus networks, etc.):
docker system prune
Use --volumes with caution to also prune unused volumes.
Security and Best Practices (Intro Level)
Full container security is a larger topic; here are some Docker‑specific fundamentals:
- Prefer minimal base images (
alpine,distroless, etc.) when appropriate. - Run as non‑root inside the container:
RUN useradd -m appuser
USER appuser- Avoid unnecessary capabilities and privileged mode; only use
--privilegedwhen you understand the consequences. - Keep images up to date: rebuild periodically from updated base images.
- Scan images for vulnerabilities with tools like
docker scanor external scanners.
Putting It Together: A Simple Example Workflow
- Develop a simple web service (code +
Dockerfile). - Build:
docker build -t myuser/simple-web:1.0.0 .- Run locally:
docker run -d -p 8080:80 --name simple-web myuser/simple-web:1.0.0- Test via
curl http://localhost:8080. - Tag and push to a registry:
docker tag myuser/simple-web:1.0.0 myregistry.local/myuser/simple-web:1.0.0
docker push myregistry.local/myuser/simple-web:1.0.0- Deploy the same image on another host that has Docker installed and access to the registry:
docker pull myregistry.local/myuser/simple-web:1.0.0
docker run -d -p 80:80 myregistry.local/myuser/simple-web:1.0.0This encapsulates the fundamental Docker pattern: build once, run anywhere that supports Docker.