codingstairs
NotesEDULifeContact
⌕Search⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

Get in touch

Send without signing in. Add your email if you'd like a reply.

  • Leave a message anonymously →
  • ✉ warragon112@gmail.com
  • KakaoTalk Open Chat ↗

© 2026 codingstairs

  • Notes
  • EDU
  • Search
  • Life
  • Contact
  • Legal
  • RSS
  • GitHub
Notes›infra

Docker Basics

Published 2026-04-28· Updated 2026-05-18·0 views

Docker Basics

A container is an isolated user space that runs on top of the OS, letting the same application run anywhere with the same shape. Docker is the decisive moment that bundled containers into a standard tool and brought them to the mainstream. This piece covers Docker's origins, basic concepts (image / container / layer / volume / network), the core Dockerfile commands, multi-stage builds, BuildKit, and neighboring tools.

1. About Docker

Event Period
dotCloud starts as an internal tool (Solomon Hykes and others) 2008–2013
Docker open-sourced (PyCon 2013) 2013-03
Docker 0.9 — libcontainer introduced (LXC dependency removed) 2014
Docker 1.0 2014-06
OCI (Open Container Initiative) launched — Linux Foundation 2015
Multi-stage build (Dockerfile) Docker 17.05 / 2017
BuildKit stabilized 2018
Docker Desktop license change (paid for large enterprises) 2021

The two specifications OCI standardized — the image spec and the runtime spec — let runtimes other than Docker (containerd, CRI-O, Podman) handle the same images.

2. Core Vocabulary

Term Meaning
Image An executable filesystem snapshot plus metadata. Immutable.
Container A process-isolation unit launched from an image. Changes go into the top layer.
Layer The unit of change in an image. The unit of cache and reuse.
Volume Persistent data outside the container.
Network Communication boundary between containers (bridge, host, overlay).
Registry Image storage (Docker Hub, ghcr.io, ECR).

3. Images and Layers

Each command in a Dockerfile (RUN, COPY, ADD) is a layer. The same command with the same input gets cache reuse. Ordering layers well makes a big difference in build time.

FROM node:22-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci          # cache invalidates here when deps change
COPY . .            # when only source changes, the layer above is reused
CMD ["node", "server.js"]

4. Core Dockerfile Commands

Command Role
FROM Base image. Used multiple times in multi-stage.
WORKDIR The current directory for subsequent commands.
COPY / ADD File copy. ADD auto-extracts URLs and tar. COPY is generally recommended.
RUN Executes at build time. Shell mode vs exec mode.
ENV Environment variable. Pinned in the image.
EXPOSE Metadata (documentation). The actual port mapping happens at runtime.
CMD Default exec command.
ENTRYPOINT Container entry point. Combines with CMD.

CMD provides default arguments, ENTRYPOINT is the fixed command. Combine them — write ENTRYPOINT ["node"] CMD ["server.js"] and the arguments can be swapped in.

5. Multi-stage Build

Separate build and runtime stages to keep the final image small.

FROM node:22 AS build
WORKDIR /src
COPY . .
RUN npm ci && npm run build

FROM node:22-slim
WORKDIR /app
COPY --from=build /src/dist ./dist
COPY --from=build /src/node_modules ./node_modules
CMD ["node", "dist/server.js"]

Build tooling, source, and test artifacts never enter the final image.

6. BuildKit

A rewritten build engine that brings parallel execution, cache mounts, and secret mounts.

# syntax=docker/dockerfile:1.7
RUN --mount=type=cache,target=/root/.npm \
    npm ci
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
    npm publish

docker buildx runs on top of BuildKit and adds multi-architecture (arm64 / amd64) builds and remote builders.

7. Other Tools

Tool Note
Podman Red Hat-led. Daemon-less model. Docker CLI compatible.
containerd The runtime Docker spun out and donated to CNCF. Adopted as the standard by Kubernetes.
nerdctl A Docker-compatible CLI for containerd.
LXC, LXD The older form of Linux containers. Emphasizes system containers.
Buildah A builder from the Podman camp. Works without a Dockerfile.

8. Choosing a Base Image

  • alpine — Small (a few MB). musl libc. Often runs into compatibility issues with glibc-based binaries.
  • distroless (Google) — Runtime only, no OS tooling. Reduces the security surface.
  • scratch — Completely empty. Suits static binaries (e.g., Go).
  • slim variants (debian:slim, node:slim) — A balance point.

9. Common Patterns

.dockerignore — similar to .gitignore. Shrinks the build context, cutting build time and image size.

node_modules
.git
*.log
.env*

Non-root user — reduces root exposure inside the container:

RUN useradd -m -u 10001 app
USER app

Healthcheck — orchestrators (compose, k8s) use it to decide on startup and restart:

HEALTHCHECK --interval=30s --timeout=3s CMD curl -fsS http://localhost:8080/health || exit 1

Stack-specific runtime slimming — leaving build tooling in the runtime stage is the most common cause of bloat. Standard one-liners by language:

  • Next.js (Node) — set output: 'standalone' in next.config.ts. The runner stage copies only .next/standalone + .next/static + public/ and runs node server.js. 60–80% smaller than copying the full node_modules.
  • FastAPI (Python) — builder/runtime 2-stage. With psycopg2-binary you don't need gcc/libpq-dev in the runtime. Download the Playwright browser (chromium) in the builder and copy it to the runtime; only run playwright install-deps (system libraries like libnss3) in the runtime.
  • Spring Boot (Java) — JDK builder → JRE runner 2-stage. Boot 4's extract --layers produces broken empty standard directories; the safe path is a single COPY *.jar app.jar with ENTRYPOINT ["java", "-jar", "app.jar"].

For apt packages, trace whether they're actually used at runtime (subprocess/spawn). In the Node world a CLI like gzip is usually replaceable by zlib.createGzip — drop the package entirely.

10. Common Pitfalls

COPY . . cache invalidation — once a single source file changes, the dependency-install step has to run again. Copy dependency files first; that's the canonical order.

Signal handling for the PID 1 process inside a container — wrappers like npm start may not forward SIGTERM to the child cleanly. Use exec mode (CMD ["node", "server.js"]) or tini.

Image size — node_modules, build cache, and OS package leftovers. Multi-stage plus a slim or distroless base makes a big difference.

Confusing Windows and Linux containers — Docker Desktop's mode switch changes the OS of the base image. On Windows, check daemon.json and the WSL2 backend setting.

The latest tag — breaks reproducibility. Pin explicit version tags or digests.

Dangling <none> images accumulate on rebuilds with the same tag — building myapp:dev again untags the previous image, leaving it as dangling. This is the normal mechanism, but without automatic cleanup the disk fills up fast. A single docker image prune -f at the end of every CI script prevents the buildup. The WSL2 backend stores everything in a sparse VHDX, so docker prune alone doesn't reclaim host disk — you also need Optimize-VHD -Mode Full or diskpart compact vdisk for the file to actually shrink.

Closing thoughts

Docker is the standard tool and starting line for containers. When multi-stage, BuildKit cache mounts, and .dockerignore are all in place, build time, image size, and reproducibility all improve together. Avoid the latest tag and pin explicit versions.

Next

  • docker-compose-patterns
  • caddy-not-nginx

Refer to the Docker docs, the Dockerfile reference, BuildKit, the OCI Image Spec, the OCI Runtime Spec, Podman, and Distroless.

More in infra

All in this category →
  • Cloud Emulator Stack — Designing a 4th Environment
  • Local HTTPS — mkcert and a Self CA
  • The Place of Single-server Operations
  • Loopback Binding and SSH Tunnel
  • Caddy and nginx — A Comparison
  • Docker Compose Patterns