1 Docker Fundamentals
A focused guide to Docker Fundamentals, connecting core concepts with practical Docker and container operations.
Docker Fundamentals covers the basic building blocks that every other Docker-related topic builds on: images, containers, the Dockerfile, the client-daemon relationship, and the core commands used to create and manage containerized applications.
The Docker Engine
At the heart of Docker is the Docker Engine, composed of a long-running background service called the daemon (dockerd) and a command-line client (docker) that sends instructions to it. The client and daemon communicate through a REST API, which means the client can control a daemon running on the same machine or on a remote host. Most day-to-day work happens through a small set of subcommands: docker build to create an image, docker run to start a container from an image, docker ps to list running containers, and docker stop or docker rm to manage their lifecycle.
Images
An image is an immutable, read-only template containing an application and everything needed to run it: a minimal operating system layer, runtime libraries, application code, and default configuration. Images are identified by a repository name and a tag, such as python:3.12-slim, where the tag typically denotes a version or variant. Images are never modified directly; instead, a new image is built whenever the application or its dependencies change.
The Dockerfile
A Dockerfile is a plain-text recipe for building an image. It begins with a FROM instruction that selects a base image, followed by instructions such as COPY to bring files into the image, RUN to execute setup commands like installing packages, ENV to set environment variables, EXPOSE to document which network ports the container listens on, and CMD or ENTRYPOINT to define what process runs when a container starts.
Each instruction adds a new layer to the image. Docker caches these layers, so rebuilding an image after a small code change is typically fast, since only the layers after the change need to be regenerated.
Containers
A container is a running, isolated instance of an image. Starting a container with docker run <image> creates a new, writable layer on top of the image and launches the process defined by the image's CMD or ENTRYPOINT. Multiple containers can be started from the same image simultaneously, each with its own isolated filesystem, network interface, and process space, while still sharing the same underlying image layers on disk.
Containers are ephemeral by design: stopping and removing a container discards its writable layer along with any data written there, unless that data was explicitly placed in a volume or bind mount.
Basic Networking and Ports
By default, a container runs on an internal Docker network and is not reachable from outside the host. The -p flag on docker run maps a port on the host to a port inside the container (for example, -p 8080:80 makes the container's port 80 available on the host's port 8080), which is the most common way to expose a web service running in a container during development.
Basic Data Persistence
Because a container's writable layer disappears when the container is removed, any data that needs to outlive the container — a database's files, for instance — should be stored in a Docker volume or mounted from the host filesystem. This separation between the application (in the image) and its data (in a volume) is one of the fundamental patterns of working with Docker.
Why These Fundamentals Matter
Understanding images, the Dockerfile, and the container lifecycle is the prerequisite for everything else in the Docker ecosystem, including networking models, orchestration with tools like Docker Compose or Kubernetes, and production deployment practices. Every more advanced Docker workflow is ultimately an extension of this same core cycle: define an image, build it, and run one or more containers from it.