170 companion flashcards · AI-assisted study content · Open the deck →
This deck offers a focused introduction to Docker and the core concepts behind working with containers. You'll find cards covering the fundamentals, like what Docker, containers, and images actually are, alongside practical questions about specific Dockerfile instructions such as FROM, RUN, COPY, ADD, EXPOSE, WORKDIR, ENV, ARG, LABEL, VOLUME, USER, and HEALTHCHECK. Several cards also explore common comparisons, such as the differences between COPY and ADD, CMD and ENTRYPOINT, and ENV and ARG, which are frequent sources of confusion for newcomers.
It's a great fit if you're just getting started with containerization, whether you're a developer preparing to package an application, a sysadmin exploring modern deployment workflows, or a student studying for a cloud or DevOps certification. If you already have some hands-on experience with docker build and docker run, the deck can still serve as a useful refresher to lock in the terminology before an interview or exam.
Because many of these cards test subtle distinctions between similar commands, you'll get the most out of them by answering from memory before peeking at the back. When you hit a comparison question, try writing out the difference in your own words before flipping the card; explaining it to yourself cements the distinction much more than passive reading does.
Finally, consider spacing your review sessions over a few days rather than cramming everything at once. The Dockerfile instructions tend to fall into logical groups, like image-building steps versus runtime configuration, so revisiting related cards in the same sitting can help you see those patterns and build a more durable mental model of how a Dockerfile comes together.
Docker is an open-source platform that automates the deployment of applications inside lightweight, portable containers. A Docker container is a runnable instance of a Docker image: an isolated process that shares the host operating system kernel but has its own filesystem, networking, and process space. Containers are ephemeral by default, meaning any data written inside them is lost when they are removed unless it is stored elsewhere. This model is fundamentally different from virtual machines. Containers share the host kernel and start in milliseconds, while VMs include a full guest operating system, take gigabytes of disk space, and boot in seconds or minutes. The trade-off is that containers provide less hardware-level isolation than VMs but offer higher density and faster startup, which is why they have become the standard unit of deployment in modern cloud environments.
The Docker Engine itself is built from three components: the dockerd daemon, which does the actual work of building images, running containers, and managing networks and volumes; a REST API that the daemon exposes; and the docker CLI client, which sends commands to the daemon over that API. The daemon and CLI typically communicate through a Unix socket at /var/run/docker.sock, but they can also run on separate machines. Underneath the daemon sits containerd, an industry-standard core container runtime that handles image transfer and storage, container execution, network attachment, and storage. Docker Engine uses containerd internally, and Kubernetes can use it directly via its CRI plugin.
The isolation that containers rely on comes from two Linux kernel features: namespaces and cgroups. Namespaces give each container its own view of system resources such as PIDs, network interfaces, mount points, hostnames, inter-process communication, and user IDs, so processes in one container cannot see resources in another by default. Cgroups limit and account for the resources a container can consume, including CPU, memory, disk I/O, and network bandwidth, preventing a single container from exhausting the host. These primitives are what allow Docker to package an application with all its dependencies and guarantee consistent behavior across environments.
A Dockerfile is a script of instructions used to build an image, and the FROM instruction sets the base image on which everything else is built. FROM must be the first non-ARG instruction in the file; for example, FROM python:3.11-slim uses an official Python image as the starting point. A special value, FROM scratch, produces an image with no parent layer at all, useful for statically linked binaries or language runtimes that ship their own. RUN executes a command during the build process and commits the result as a new layer; the best practice is to chain commands with && to keep the number of layers small. COPY simply moves files and directories from the build context into the image, while ADD does the same plus supports automatic extraction of local tar archives and fetching files from URLs. Because ADD's extra features can be surprising, the official guidance is to prefer COPY unless those features are explicitly needed.
CMD and ENTRYPOINT define what a container runs when it starts, but they have different roles. ENTRYPOINT sets the main executable that always runs, while CMD provides default arguments that can be overridden at runtime. When both are used together, CMD supplies the defaults to ENTRYPOINT, and a common pattern is ENTRYPOINT ["python"] combined with CMD ["app.py"]. This separation lets users pass arguments at run time that get appended to the entrypoint, or override CMD entirely. A related instruction, EXPOSE, documents which ports the container listens on, but it does not actually publish them; that happens at runtime with docker run -p. The WORKDIR instruction sets the working directory for all subsequent RUN, CMD, ENTRYPOINT, COPY, and ADD instructions, creating the directory automatically if it does not exist. ENV and ARG define variables: ENV persists into the running container, while ARG is available only during the build.
Several instructions focus on metadata, persistence, security, and health. LABEL adds key-value metadata such as maintainer or version, inspectable with docker inspect. VOLUME declares a mount point for externally attached storage so that data survives beyond the container lifecycle. USER sets the user (and optionally group) under which subsequent instructions and the container process run, which is a security best practice for avoiding root. HEALTHCHECK tells Docker how to test whether the container is still working, exposing healthy, unhealthy, or starting states. STOPSIGNAL sets the system signal sent during docker stop (default SIGTERM), SHELL overrides the default /bin/sh used for shell-form commands, and ONBUILD registers a trigger that runs in any downstream image built FROM this one. A final important nuance is the difference between exec form (CMD ["python", "app.py"], which runs directly and forwards signals correctly) and shell form (CMD python app.py, which runs via /bin/sh -c and may swallow signals). Always prefer exec form for CMD and ENTRYPOINT.
Every instruction in a Dockerfile produces a read-only layer, and layers are stacked to form the final image. When a container runs, a thin writable layer is added on top. Layers are cached: if an instruction and its context are unchanged, Docker reuses the cached layer instead of running the command again. This is why ordering matters, because putting frequently changing instructions late in the file (such as COPY . . near the end) maximizes cache reuse and keeps build times fast. A related tool, the .dockerignore file in the build directory, excludes files and directories from the build context sent to the daemon, preventing large folders like node_modules or sensitive files like .env from inflating the context or leaking into images.
For more demanding builds, Docker provides BuildKit, the next-generation build engine that is enabled by default in recent versions. BuildKit brings parallel build steps, better caching, rootless builds, and three especially useful mount types in RUN instructions: type=cache mounts a persistent build cache at a path inside the build container (great for package-manager caches like /root/.cache/pip), type=secret makes a secret available at build time without baking it into any layer, and type=ssh forwards the host's SSH agent so private repositories can be cloned during the build. The docker buildx command is the extended CLI built on top of BuildKit, supporting multi-platform builds in a single command, advanced caching backends (registry, S3, local), and custom builders.
Multi-stage builds take advantage of BuildKit and the layered model. A Dockerfile can declare multiple FROM instructions, each starting a new stage with its own base image, and copy only the needed artifacts from earlier stages into the final image. This produces much smaller production images: a builder stage with a full toolchain can compile code, and the final stage contains only the compiled binary on a minimal base. Named stages can also be built independently using docker build --target, which is useful in CI pipelines. Multi-platform builds with buildx produce a manifest list (or image index), a single reference that points to platform-specific images so the runtime can pull the right variant for its architecture.
Once an image is built, it is shared through registries that implement the OCI distribution spec. Docker Hub is the official public registry, hosting official images curated in partnership with upstream projects (such as nginx, postgres, python), automated builds from GitHub and Bitbucket, public and private repositories, and vulnerability scanning. Verified publishers add an extra layer of trust, since Docker has confirmed their identity. For private distribution, you can run your own registry with docker run -d -p 5000:5000 registry:2, tag images for that endpoint, and push them after docker login. Because Docker images follow the OCI image format, they are portable to any compliant runtime such as Podman, containerd, or CRI-O. In security-sensitive environments, Docker Content Trust uses Notary to sign image tags so that only trusted publishers' images can be pulled when DOCKER_CONTENT_TRUST=1.
The docker command-line tool is the day-to-day interface for working with containers and images. docker run creates and starts a new container from an image, accepting many flags at once: -d for detached mode, -p for port mapping, -v for volume mounts, --name to assign a name, -e for environment variables, --network to choose a network, --restart for restart policies, and many more. docker build creates an image from a Dockerfile in the current directory, with -t to tag, -f to specify a different Dockerfile, --no-cache to ignore cache, and --build-arg to pass ARG values. Once containers are running, docker ps lists them (with -a to include stopped ones, -q for IDs only, and --filter to narrow by status), docker logs retrieves their stdout and stderr (with -f to follow, --tail to limit output, --since for time ranges, and -t for timestamps), and docker exec runs a new command inside a running container, commonly docker exec -it mycontainer bash for an interactive shell.
A container's lifecycle includes several states: created (docker create), running (docker start or docker run), paused (docker pause, which uses the cgroup freezer to suspend processes without freeing memory), stopped (docker stop, which sends SIGTERM then SIGKILL after a default 10-second grace period), and removed (docker rm). The difference between docker stop and docker kill is that kill sends a signal immediately (default SIGKILL) with no grace period, while stop allows graceful shutdown. docker rm -f forces removal of a running container, docker rm -v also removes anonymous volumes attached to the container, and docker container prune cleans up all stopped containers. To restart a container with new resource limits, docker update can change --memory, --cpus, and the restart policy without recreating it.
For moving images and data around, the CLI offers a rich set of subcommands. docker pull and docker push transfer images between registries, docker save and docker load export images to and from tarballs (useful for air-gapped environments), and docker export and docker import work on flattened container filesystems without layers or history. docker cp copies files between a container and the host in either direction, docker diff shows filesystem changes inside a container, docker top lists running processes, docker port shows current port mappings, and docker stats streams live resource usage. docker tag adds a new tag to an existing image without copying data, docker rmi removes images (or docker image rm as the modern form), and docker commit creates an image from a container's current state, though this is considered an anti-pattern because it bypasses Dockerfile reproducibility. Cleanup is handled by the family of prune commands: docker system prune, docker image prune (add -a for all unused images, not just dangling), docker container prune, and docker volume prune. The umbrella command docker system df shows disk usage, docker info reports daemon configuration, and docker context lets you switch between multiple Docker hosts.
Docker networking is built around several drivers, each with a different isolation and use case. The bridge driver is the default: it creates an isolated network on a single host, and containers attached to it can communicate by IP. User-defined bridges (created with docker network create mynet) are recommended over the default bridge because they support automatic DNS resolution by container name, allow attaching and detaching running containers, and are fully configurable. The host driver removes network isolation entirely, sharing the host's network namespace so the container uses the host's IP and ports directly, which is useful for performance-sensitive workloads but reduces isolation and is only available on Linux. The overlay driver enables communication between containers on different Docker hosts in a Swarm cluster, using VXLAN encapsulation to create a distributed network.
Two more drivers handle specialized scenarios. The none driver attaches only a loopback interface, giving the container no external connectivity at all, which is useful for batch jobs and security-sensitive sandboxes. macvlan assigns a container a unique MAC address so it appears as a physical host on the LAN, while ipvlan shares the host's MAC and uses L3/L4 separation, working in environments that restrict MAC addresses such as some cloud providers.
Name resolution is one of the most important practical differences between network types. On a user-defined bridge network, Docker runs an embedded DNS server (reachable at 127.0.0.11 inside containers) that resolves container names, network aliases, and service names. This means a web container can reach a db container simply by calling its name. On the default bridge, however, this does not work; only IPs can be used to reach other containers. Network aliases add an extra DNS name to a container on a specific network, which is useful when the same service is reachable under different names from different networks. Containers can be attached to additional networks at runtime with docker network connect and detached with docker network disconnect. Port publication is a separate concern: EXPOSE in a Dockerfile is documentation only, while actual publication happens at runtime with docker run -p 8080:80, or with -P (uppercase) which publishes every EXPOSEd port to random host ports. Default IP ranges for bridge networks fall in the 172.x.0.0/16 space, with the host at .0.1, and these can be overridden per network with --subnet and --gateway.
Containers are ephemeral by default, but Docker provides several mechanisms for persisting data outside the container's writable layer. A Docker volume is a persistent storage object managed by Docker itself and stored in /var/lib/docker/volumes/. There are three flavors: named volumes (created explicitly with docker volume create mydata and easy to back up, list, and reuse), bind mounts (which map an arbitrary host path directly into a container and are favored in development for live-reload workflows), and tmpfs mounts (which live in the host's RAM and swap, making them extremely fast but non-persistent, and ideal for secrets or temporary caches). The VOLUME instruction in a Dockerfile declares a mount point for externally attached storage, and docker run --tmpfs /tmp:size=100m attaches a memory-backed mount at /tmp.
Anonymous volumes are created implicitly when a Dockerfile declares VOLUME without a name or when you run docker run -v /data. Docker generates a random name, which makes them hard to reference later, so the recommended pattern is to use named volumes in production. When a container is removed, its volumes are not deleted by default, which protects data, but you can use docker rm -v to also clean up anonymous volumes associated with a container, or docker volume prune to remove all unused volumes. Volume drivers and plugins extend Docker to mount external storage such as NFS, AWS EBS, Azure Disk, GCE Persistent Disk, and Ceph; the built-in local driver handles simple cases and others are installed separately. Bind mounts also have a propagation mode (rprivate, private, rshared, shared, slave, rslave) that controls how mounts created inside a mount propagate to the host, though most users should leave this at its default.
Docker Compose is a tool for defining and running multi-container applications using a YAML file, traditionally named docker-compose.yml. The top-level structure has three sections: services (the containers themselves), networks (how they communicate), and volumes (shared storage). A service is defined under the services key and typically includes a build directive pointing at a directory with a Dockerfile, an image directive to use an existing image, port mappings, volume mounts, environment variables, and dependencies on other services. A typical stack might define a web service that builds from the current directory and publishes port 8080, alongside a database service that uses the postgres:15 image and mounts a named volume for its data.
Several features make Compose practical for real applications. depends_on controls startup order, ensuring a service starts only after its declared dependencies; however, by default it waits only for the container to start, not for the service to be ready, and for that you pair depends_on with condition: service_healthy and a healthcheck on the dependency. Environment variables can be set inline under environment:, loaded from a per-service env_file, or interpolated from shell variables using the ${VAR} syntax. A .env file in the same directory as the Compose file is loaded automatically, though it is distinct from env_file (which is loaded inside the container). Networks are declared at the top level and referenced by services, and services on the same network can resolve each other by name. The Compose Specification is the current open standard for the file format, making a single docker-compose.yml portable across Docker, AWS ECS, Microsoft ACI, and Kubernetes via kompose.
A few advanced features round out Compose. Profiles group services so they only start when explicitly requested with --profile, which is useful for dev-only services like debuggers or load generators. Scaling a service is done with docker compose up -d --scale web=3 for non-deploy keys, or with deploy.replicas when running in compatibility mode. The extends directive lets a service inherit configuration from another, while include (newer) merges whole YAML files into the project, which is the modern way to split Compose configurations. YAML anchors and aliases reduce duplication in large files by letting a fragment be defined once and reused across services. Common commands include docker compose up -d to start in detached mode, docker compose --build to rebuild images, docker compose down to stop and remove everything, docker compose logs to view output, and docker compose --profile debug up to activate a profile.
Running containers securely starts with a handful of well-known practices. Containers should run as a non-root user, set via the USER instruction in the Dockerfile. Base images should be minimal, such as alpine, slim Debian variants, or distroless images, to reduce both attack surface and image size. Image layers should be scanned for vulnerabilities with tools like docker scout. Secrets should never be baked into image layers; instead, pass them at runtime with -e or env_file, or use BuildKit's --mount=type=secret during the build. The --read-only flag mounts the container's root filesystem as read-only so that only tmpfs and explicit volume paths can be written to, which is a strong hardening technique. Dropping Linux capabilities that the container does not need further narrows the surface: Docker keeps a small safe set by default and exposes --cap-add and --cap-drop for tuning.
A few options deserve special caution. --privileged gives the container almost all host capabilities and access to all devices, disabling most security barriers, which is useful for nested Docker-in-Docker setups but a major risk in production. The --security-opt flag enables additional protections: no-new-privileges prevents setuid binaries from gaining new privileges, while seccomp and apparmor options load custom security profiles. Docker's default seccomp profile allows about 300 syscalls and blocks the rest. The userns-remap setting in daemon.json maps container UIDs to a range of unprivileged host UIDs, so that even a container running as root does not yield host root in the event of an escape.
Resource limits are essential for stable multi-tenant systems. docker run --memory=512m caps memory, --cpus=1.5 limits CPU usage, --pids-limit caps process creation (defending against fork bombs), --shm-size enlarges /dev/shm beyond its default 64 MB (which fixes shared-memory errors in Python multiprocessing and some ML workloads), and --ulimit nofile=65535:65535 raises the open-file limit beyond the default 1024. In Compose, these are expressed under deploy.resources.limits. Restart policies control what happens on exit: no (do not restart), on-failure (only on non-zero exit), always (always restart, even after docker stop), and unless-stopped (always except after a manual stop).
A HEALTHCHECK instruction or flag gives Docker a way to test whether the container is genuinely working, distinguishing "running" (the main process is alive) from "healthy" (the healthcheck reports 0), which orchestrators use to gate dependent services. Signal handling deserves attention as well: PID 1 inside a container ignores SIGTERM by default unless the process is signal-aware, which is why exec form is preferred for CMD and ENTRYPOINT and why --init (which injects tini as PID 1) is recommended for most images. Log rotation is another operational concern: the default json-file driver has no rotation and can fill the disk, so configuring max-size and max-file log-opts in daemon.json keeps usage bounded.
FROM node:18 AS builderFROM nginx:alpineCOPY --from=builder /app/build /usr/share/nginx/htmlservices key in docker-compose.yml:services:
web:
build: .
ports:
- "8080:80"
db:
image: postgres:15
volumes:
- pgdata:/var/lib/postgresql/datadockerd) that manages images, containers, networks, and volumes; a REST API that the daemon exposes; and the CLI client (docker) that talks to the daemon via the API. The daemon and client can run on the same host or on different machines.docker kill sends a signal (default SIGKILL) to a running container's main process, terminating it immediately. docker stop first sends SIGTERM and waits for graceful shutdown before sending SIGKILL. Use docker kill -s SIGTERM ctr to send a specific signal.DOCKER_BUILDKIT=1 or in /etc/docker/daemon.json.version top-level key (e.g., "3.8") historically selected the Compose file schema. With Compose Spec (now the standard), version is deprecated and ignored. Modern Compose files omit it.docker network connect mynet mycontainer attaches a running container to an additional network. The container then has an interface on both networks. docker network disconnect mynet mycontainer removes it.RUN --mount=type=cache,target=/go/pkg/mod, BuildKit mounts a persistent cache directory across builds. Unlike a normal RUN layer, the cache is reused even if the surrounding RUN changes — making Go module downloads and similar workflows fast.--pids-limit to cap the number of processes a container can spawn (default is usually 4096). Useful for defending against fork bombs and runaway process creation. docker run --pids-limit=100 stress-container.Drill this topic
170 flashcards on Docker Containers — free, no signup needed to start.
Study Docker Containers flashcardsLearnWiki pages are generated with AI assistance from LearnCoachAssist's reviewed study catalog and may contain errors — verify anything critical against your course materials.