Docker Containers (Dockerfile, Multi-stage Builds, Volumes, Networks & Security)

Deep-dive Docker and OCI containerization mastery covering Linux kernel namespaces and cgroups, OverlayFS layers, Dockerfile instructions (CMD vs ENTRYPOINT, COPY vs ADD, ARG vs ENV), multi-stage builds, storage (Volumes vs Bind Mounts), container networking, and container security hardening.

Level
basic
Languages
en → en
Cards
32
Markdown
Docker Containers (Dockerfile, Multi-stage Builds, Volumes, Networks & Security) (.md)

Flashcards (32)

#Front (Term / Question)Back (Definition / Answer)ExampleMultiple-Choice Options
1Why do processes running as `PID 1` inside a container need special signal and zombie-reaping handling (e.g., `docker run --init` or `tini`)?In Linux, `PID 1` has two unique kernel responsibilities: 1. The kernel ignores default signal handlers (like `SIGTERM`) on `PID 1` unless the process explicitly registers a signal handler in code; 2. If any child process inside the container becomes orphaned, it is re-parented to container `PID 1`—if `PID 1` (e.g., Java, Node, Python, or bash script) does not call `wait()` to reap dead children, zombie processes accumulate. Passing `--init` (or using `tini` as `ENTRYPOINT`) runs a tiny init wrapper as `PID 1` that forwards signals and reaps zombies.
2What happens when a Docker container exits with code `137` vs. `143` vs. `1` vs. `126`/`127`?In Linux/POSIX, exit codes `> 128` indicate termination by signal $128 + N$: `137` ($128 + 9 = \text{SIGKILL}$): The container was forcefully killed—most commonly by the Linux Kernel Out-Of-Memory (OOM) Killer (verify via `docker inspect <id> --format '{{.State.OOMKilled}}'`) or because it ignored `SIGTERM` during `docker stop`. `143` ($128 + 15 = \text{SIGTERM}$): Graceful shutdown via `SIGTERM`. `1`: General application error. `126`: Command found but not executable (permission error). `127`: Command/binary not found in `$PATH`.
3Explain the architecture chain from `docker` CLI -> `dockerd` -> `containerd` -> `containerd-shim` -> `runc`.1. `docker` CLI sends REST API requests over `/var/run/docker.sock` to `dockerd`. 2. `dockerd` delegates container lifecycle management via gRPC to `containerd`. 3. For each container, `containerd` spawns a lightweight `containerd-shim` process. 4. `containerd-shim` invokes `runc` (the low-level OCI runtime) to create the Linux namespaces and cgroups and start the container process. 5. Once the container starts, `runc` exits, leaving `containerd-shim` as the container's parent—allowing `dockerd`/`containerd` to be upgraded/restarted without killing running containers ('live restore').
4What are Linux Capabilities in Docker, and why is `--cap-drop=ALL` far safer than `--privileged`?The Linux kernel splits traditional omnipotent `root` privileges into ~40 distinct Capabilities (e.g., `CAP_NET_BIND_SERVICE` to bind ports <1024, `CAP_NET_ADMIN` to modify network interfaces, `CAP_SYS_ADMIN`, `CAP_CHOWN`). Docker grants a reduced default set of ~14 capabilities. Running `--privileged` grants ALL capabilities and exposes all host `/dev` devices (allowing trivial host takeover via mounting the host disk). Best practice: Drop all capabilities with `--cap-drop=ALL` and add back only what is strictly needed (e.g., `--cap-add=NET_BIND_SERVICE`).
5How do `--memory`, `--memory-swap`, and `--cpus` work in `docker run` using Linux `cgroups`?`--memory=512m` sets a hard RAM limit in the memory cgroup; if the container exceeds 512 MiB (plus allowed swap), the kernel OOM Killer terminates it (`Exit 137`). Setting `--memory=512m --memory-swap=512m` disables swap completely for the container (since `--memory-swap` represents total RAM + swap). `--cpus=1.5` uses the CFS (Completely Fair Scheduler) quota (`cpu.cfs_quota_us` / `cpu.cfs_period_us`) to throttle the container to at most 1.5 CPU cores' worth of CPU time per 100ms period without killing it.
6List and explain the seven Linux Kernel Namespaces used by container runtimes (`runc` / `containerd`).1. `pid`: Isolates the Process ID number space (the container's entrypoint becomes `PID 1` inside the container); 2. `net`: Isolates network interfaces, loopback, routing tables, and `iptables`/ports; 3. `mnt`: Isolates filesystem mount points (`chroot`/`pivot_root`); 4. `uts`: Isolates hostname and NIS domain name; 5. `ipc`: Isolates System V IPC and POSIX message queues; 6. `user`: Maps root (`UID 0`) inside the container to an unprivileged non-root UID on the host; 7. `cgroup`: Isolates the view of the cgroup hierarchy.
7Compare Docker Volumes, Bind Mounts, and `tmpfs` mounts for container storage.1. Volumes (`docker run -v myvol:/data`): Managed by Docker in `/var/lib/docker/volumes/` on the host; isolated from host path structure, supports volume drivers (NFS/cloud), safe to share across containers, and populates empty volumes with existing container directory contents. 2. Bind Mounts (`-v /host/path:/container/path` or `--mount type=bind`): Mounts an exact file/directory path from the host OS into the container (hides existing container directory contents). 3. `tmpfs` Mounts (`--tmpfs /app/cache`): Stored strictly in host RAM, never written to disk, erased when container stops.
8Compare the five Docker Networking drivers: `bridge`, `host`, `none`, `overlay`, and `macvlan`.1. `bridge` (default): Creates virtual Ethernet pairs (`veth`) connected to a Linux bridge (`docker0` or custom bridge) with NAT (`iptables` MASQUERADE) to the host NIC. 2. `host` (`--network host`): Removes network namespace isolation; container binds directly to the host's IP and ports. 3. `none`: Only a loopback (`lo`) interface, zero external networking. 4. `overlay`: VXLAN encapsulation connecting containers across multiple physical hosts (Swarm/multi-host). 5. `macvlan`: Assigns a unique MAC address and physical LAN IP directly to the container.
9How does Docker's OverlayFS (`overlay2`) storage driver work with `lowerdir`, `upperdir`, `merged`, and Copy-on-Write (CoW)?Container images consist of immutable, read-only layers stacked as `lowerdir` (shared across all containers running the same image). When a container starts, Docker adds a thin writable container layer on top called `upperdir`, and presents the unified view at `merged`. Under Copy-on-Write (CoW), if a container modifies an existing file from a read-only image layer, `overlay2` copies that file up into `upperdir` and modifies the copy, leaving the underlying image layer untouched.
10Compare `COPY` and `ADD` instructions in a `Dockerfile` and state which is preferred.`COPY` strictly copies local files and directories from the build context into the container image—it is explicit, predictable, and ALWAYS PREFERRED by Docker best practices. `ADD` has two extra magic behaviors: 1. It automatically extracts local compressed tar archives (`.tar.gz`, `.tar.bz2`, `.tar.xz`) into the destination directory; 2. It can fetch remote URLs (though `curl`/`wget` + `rm` in a `RUN` layer is often preferred or `ADD --checksum` in BuildKit).
11Compare `ARG` and `ENV` instructions in a `Dockerfile`, and explain why `ARG` must NEVER be used for secrets.`ARG` defines build-time variables (passed via `docker build --build-arg VAR=val`) that are available ONLY during `docker build` and do NOT persist in the running container environment. `ENV` defines environment variables that exist both during build AND persist inside running containers (overridable via `docker run -e`). WARNING: `ARG` values are baked in plain text into the image's layer history (`docker history <image>`), so passing secrets via `ARG` leaks them! Use BuildKit secret mounts (`RUN --mount=type=secret,id=mysecret`) instead.
12What is the critical difference between Exec Form (`["executable", "param1"]`) and Shell Form (`executable param1`) in `CMD` and `ENTRYPOINT`?Shell Form (`CMD node server.js`) wraps the command in `/bin/sh -c`, making `/bin/sh` run as `PID 1` inside the container instead of your application! Because `/bin/sh` does not forward POSIX signals like `SIGTERM` (sent by `docker stop` or Kubernetes pod termination) to child processes, your app never receives `SIGTERM`, hangs for the 10s/30s grace period, and gets killed violently with `SIGKILL` (`137`). Exec Form (`CMD ["node", "server.js"]`) runs your app directly as `PID 1` so it receives `SIGTERM` cleanly.
13Compare the four Docker container restart policies (`no`, `on-failure`, `always`, `unless-stopped`).`no` (default): Never automatically restart the container. `on-failure[:max-retries]`: Restart ONLY if the container exits with a non-zero exit code (indicating an error). `always`: Always restart the container regardless of exit code—even if you manually ran `docker stop`, it will start back up when the Docker daemon restarts! `unless-stopped`: Always restart the container when it crashes or on daemon reboot, UNLESS the container was explicitly placed into a stopped state by a user before the daemon restarted.
14What is the Docker Build Context and what role does `.dockerignore` play?When you run `docker build .`, the Docker CLI archives and sends the directory contents (the 'Build Context') to the Docker daemon/BuildKit builder. Without a `.dockerignore` file, massive folders like `.git/`, `node_modules/`, `venv/`, build artifacts, and sensitive `.env` or `.aws` files are uploaded to the builder and can accidentally be baked into the image via `COPY . .`, causing slow builds, broken layer caches, and credential leaks.
15Why is running containers as `root` (default) dangerous, and how do you enforce non-root execution in a `Dockerfile`?By default, `root` (`UID 0`) inside a standard container is the exact same `UID 0` (`root`) on the underlying Linux host kernel! If a container breaks out via a kernel vulnerability or mounts a host directory, the attacker has root privileges on the host. Mitigation: Create a dedicated group and user in the `Dockerfile` (`RUN groupadd -r app && useradd -r -g app -u 10001 app`), set file ownership (`COPY --chown=app:app`), and switch context with `USER 10001:10001` before `ENTRYPOINT`.
16Why should an Image Digest (`image@sha256:...`) be used instead of a mutable Image Tag (`image:latest` or `image:1.2`) in high-security production deployments?Docker/OCI Image Tags (even semantic version tags like `v1.2.0`, not just `:latest`) are MUTABLE pointers in a registry—someone can overwrite `v1.2.0` with a different or compromised build tomorrow, resulting in different nodes running different code. An Image Digest (`sha256:4a8b...`) is an immutable cryptographic SHA-256 hash of the image manifest; pinning by digest guarantees bit-for-bit reproducibility across every node.
17Why should `apt-get update` and `apt-get install` ALWAYS be combined into a single `RUN` instruction alongside cache cleanup?1. Cache Busting Bug: If `RUN apt-get update` is on its own line, Docker will cache that layer for weeks; later when you add a package to a separate `RUN apt-get install` line, Docker reuses the stale cached package index and fails with `404 Not Found`. 2. Layer Bloat: Deleting `/var/lib/apt/lists/*` in a LATER `RUN` layer does not reduce image size because the files are already committed to the previous immutable layer! Always combine: `RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*`.
18What is a Multi-Stage Build in Docker and why is it essential for production images?A Multi-Stage `Dockerfile` uses multiple `FROM` statements (e.g., `FROM golang:1.22 AS builder` followed by `FROM gcr.io/distroless/static-debian12`). You compile source code, install SDKs/compilers (`gcc`, `maven`, `npm`), and run tests in the heavy builder stage, then use `COPY --from=builder /app/bin /app/bin` to copy ONLY the compiled artifact into a minimal final runtime image—slashing image size by 80–98% and eliminating compilers, shells, and CVEs from production.
19How do you reclaim disk space on a Docker host and inspect what is consuming storage under `/var/lib/docker`?Run `docker system df` (or `docker system df -v`) to inspect disk usage broken down by Images, Containers, Local Volumes, and Build Cache. Run `docker system prune` to remove stopped containers, unused networks, dangling (untagged) images, and build cache; add `-a` (`--all`) to remove all unused images not referenced by any container, and `--volumes` to remove unused anonymous/named volumes.
20How does a User-Defined Bridge network (`docker network create mynet`) differ from the default `bridge` (`docker0`) network?On the default `docker0` bridge network, containers can only communicate with each other by raw IP address (no automatic DNS resolution by container name). On a User-Defined Bridge network, Docker runs an embedded DNS server at `127.0.0.11`, enabling automatic service discovery where containers resolve each other by container name or `--network-alias`, plus providing better network isolation and dynamic online `docker network connect/disconnect`.
21What is the difference between `docker compose up`, `docker compose down`, and `docker compose down -v`?`docker compose up -d` creates the project's custom bridge network, creates named volumes, and starts all services defined in `compose.yaml` in detached mode. `docker compose down` stops and removes the containers and removes the project network, but PRESERVES named volumes so database data is not lost. `docker compose down -v` (`--volumes`) also deletes all named volumes declared in the `volumes:` section, permanently wiping stateful container data.
22Compare `CMD` and `ENTRYPOINT` in a `Dockerfile`, and explain how they interact when both are specified.`ENTRYPOINT` configures the container to run as a specific executable binary; `CMD` provides default arguments (or a default command if `ENTRYPOINT` is not set). When ANY trailing arguments are passed to `docker run <image> <args>`, they OVERRIDE `CMD`, whereas `ENTRYPOINT` is NOT overridden unless `--entrypoint` is explicitly passed. Best practice: combine Exec-form `ENTRYPOINT ["nginx"]` with `CMD ["-g", "daemon off;"]` so `CMD` acts as default flags passed into `ENTRYPOINT`.
23What does the `HEALTHCHECK` instruction in a `Dockerfile` do, and what are the three health states of a container?`HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 CMD curl -f http://localhost:8080/healthz || exit 1` instructs Docker to periodically run a command inside the container to verify the application is actually serving requests (not just deadlocked while `PID 1` stays alive). The three states are: `starting` (during grace period), `healthy` (command returns `0`), and `unhealthy` (command returns `1` for `retries` consecutive times).
24How do you debug a running `Distroless` or `scratch` container that has no `/bin/sh` or debugging utilities installed?Because `docker exec -it <container> sh` fails when there is no shell inside the target container, you can attach a debug container sharing the target container's PID and network namespaces: `docker run -it --rm --pid=container:<target> --net=container:<target> busybox sh` (or use `docker debug` / `kubectl debug -it <pod> --image=busybox --target=<container>` in Kubernetes Ephemeral Containers). From the debug container, the target's filesystem is accessible at `/proc/1/root/`!
25What is the difference between `EXPOSE 8080` in a `Dockerfile` and `-p 80:8080` (`--publish`) vs. `-P` in `docker run`?`EXPOSE 8080` in a `Dockerfile` is purely metadata/documentation—it does NOT actually publish or open the port on the host machine! To make the container reachable from outside the host, you must run `docker run -p 80:8080` (which creates an `iptables`/proxy rule mapping host port `80` to container port `8080`, or bind to localhost only via `-p 127.0.0.1:80:8080`). `-P` (`--publish-all`) binds every `EXPOSE`d port to a random ephemeral high port on the host.
26How does Docker Layer Caching work during `docker build`, and how should you order instructions in a `Dockerfile` to maximize cache hits?Docker caches each instruction layer sequentially; as soon as ONE layer's cache is invalidated (e.g., a file in `COPY` changed), ALL subsequent layers in the `Dockerfile` must be rebuilt from scratch. Therefore, order instructions from LEAST frequently changing at the top to MOST frequently changing at the bottom: 1. Base OS packages (`RUN apt-get...`); 2. Copy ONLY dependency manifests (`COPY package.json package-lock.json ./`); 3. Install dependencies (`RUN npm ci`); 4. Copy application source code (`COPY . .`) last.
27How do `--read-only`, `--security-opt=no-new-privileges:true`, and `seccomp` profiles harden a running container?`--read-only` mounts the container's root filesystem (`upperdir`) as strictly read-only so an attacker exploiting RCE cannot write backdoors or modify binaries (pair with `--tmpfs /tmp` for scratch space). `--security-opt=no-new-privileges:true` prevents container processes from gaining extra privileges via SUID/SGID binaries. Docker's default `seccomp` (Secure Computing Mode) profile blocks ~44 dangerous Linux system calls (out of 300+, such as `kexec_load`, `mount`, `reboot`, `init_module`) at the kernel syscall boundary.
28What is the Open Container Initiative (OCI) and what are its three specifications (`runtime-spec`, `image-spec`, `distribution-spec`)?The OCI (under the Linux Foundation) defines vendor-neutral standards for containers: 1. `image-spec`: Defines how an OCI container image manifest, image index (multi-arch), filesystem layer tarballs, and JSON config are structured; 2. `runtime-spec`: Defines how a low-level OCI runtime (like `runc`, `crun`, or `youki`) unpacks a filesystem bundle and launches a container using a `config.json`; 3. `distribution-spec`: Standardizes the HTTP Registry API for pushing and pulling OCI images and artifacts.
29How do Containers differ architecturally from Virtual Machines (VMs), and what two Linux kernel features make containers possible?Virtual Machines run a full Guest Operating System and kernel on top of virtualized hardware managed by a Hypervisor (KVM, Hyper-V, ESXi), incurring higher boot time and memory overhead. Containers share the Host OS Linux Kernel directly and isolate processes using two core kernel features: 1. Namespaces (which isolate what a process can SEE), and 2. Control Groups / `cgroups` (which limit how much hardware resources—CPU, RAM, Block I/O, PIDs—a process can USE).
30Compare Distroless base images, Alpine Linux (`musl` libc), and Scratch images.`FROM scratch`: Empty 0-byte image containing literally nothing (only works for 100% statically linked binaries like Go/Rust with `CGO_ENABLED=0`; note: lacks CA certificates and `/tmp` unless copied in). `Alpine` (~5 MB): Includes `busybox` shell and `apk` package manager, but uses `musl libc` instead of `glibc`, which can cause subtle DNS resolution or C-extension compatibility/performance issues in Python/Node/JVM. `Distroless` (Google): Contains ONLY the minimal `glibc`/runtime, CA certs, and `/etc/passwd`—no shell (`/bin/sh`) and no package manager.
31Why is mounting `/var/run/docker.sock` inside a container (e.g., for CI/CD runners or monitoring tools) equivalent to giving that container full `root` access to the host?`/var/run/docker.sock` is the Unix domain socket that the root-owned `dockerd` daemon listens on without authentication. Any container with write access to `docker.sock` can issue API calls to spawn a new container on the host with `--privileged -v /:/host_root`, `chroot /host_root`, and immediately take full uninhibited `root` control over the underlying host OS. For building images in CI/CD, use rootless BuildKit, Kaniko, or ephemeral VMs instead.
32How do you build Multi-Architecture (`linux/amd64` and `linux/arm64`) container images using Docker Buildx?Docker Buildx (powered by Moby BuildKit) supports cross-compilation and QEMU user-mode emulation to build multi-platform OCI Manifest Lists (Image Indexes). Command: `docker buildx create --use && docker buildx build --platform linux/amd64,linux/arm64 -t myorg/app:v1.0 --push .` Using `$TARGETOS` and `$TARGETARCH` automatic build args inside a Go/Rust `Dockerfile` avoids slow QEMU emulation by running native cross-compilers on the host architecture.