Docker¶
Docker builds, distributes, and runs OCI-compatible container images. Learn the image and process model first; Docker commands are controls over that model, not a replacement for it.
Essential Workflow¶
docker build -t example/app:dev .
docker run --rm -p 8080:8080 example/app:dev
docker ps
docker logs <container>
docker inspect <container>
docker exec -it <container> sh
docker stop <container>
Use docker exec for temporary diagnosis, not to repair production state manually. Rebuild and redeploy the image so the fix is reproducible.
A Small Production Dockerfile¶
# syntax=docker/dockerfile:1
FROM python:3.13-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
FROM python:3.13-slim
RUN useradd --create-home --uid 10001 app
WORKDIR /app
COPY --from=build /wheels /wheels
COPY requirements.txt .
RUN pip install --no-cache-dir --no-index --find-links=/wheels -r requirements.txt \
&& rm -rf /wheels
COPY --chown=app:app . .
USER app
EXPOSE 8080
CMD ["python", "-m", "app"]
Adapt the base and package installation to the application. Pin dependencies through the ecosystem's lock or hash mechanism and regularly rebuild from a maintained base.
Build Rules¶
- Keep
.dockerignorenarrow enough to exclude.git, local environments, caches, secrets, and build output. - Copy dependency manifests before frequently changing source to preserve useful cache layers.
- Combine commands only when they form one installation transaction and cleanup belongs with it.
- Use multi-stage builds to keep compilers and source-only tooling out of runtime images.
- Use BuildKit secret or SSH mounts for build credentials;
ARG,ENV, copied files, and later deletion can leave secrets in image history. - Build once and promote the same digest through environments.
The maintained Docker build guidance is the source for current builder behavior.
Dockerfile Semantics¶
FROMselects a base and begins a stage.RUNexecutes during image construction and creates a layer.COPYadds build-context files.WORKDIRsets the working directory for later instructions.ENVpersists runtime environment defaults; do not store secrets there.USERsets the runtime user.ENTRYPOINTdefines the executable;CMDprovides its default command or arguments.EXPOSEdocuments a port but does not publish it.
Prefer JSON/exec form for the main process so it receives signals directly:
CMD ["server", "--port", "8080"]
Storage¶
- writable container layer: ephemeral, tied to the container;
- named volume: engine-managed persistent storage;
- bind mount: a specific host path made visible in the container;
- tmpfs: memory-backed temporary data where supported.
Bind mounts couple workloads to host paths and permissions. Volumes do not create backups by themselves. Never assume deleting a container deletes or preserves its data—inspect the mount type and lifecycle.
Networking¶
Create a user-defined network for name-based discovery between containers:
docker network create app-net
docker run -d --name db --network app-net postgres:17
docker run --rm --network app-net example/app:dev
Inside the application container, localhost means that container, not the database or host. Publish only required ports, and bind to a specific host interface when exposure should be limited.
Compose for Local Multi-Container Work¶
services:
app:
build: .
ports: ["8080:8080"]
depends_on: [db]
db:
image: postgres:17
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets: [db_password]
volumes: [db-data:/var/lib/postgresql/data]
secrets:
db_password:
file: ./local-db-password.txt
volumes:
db-data:
depends_on controls startup order, not application readiness. The application still needs connection retries with a bounded deadline. Compose is useful for development and small deployments; it is not a Kubernetes compatibility layer.
Resource and Security Controls¶
docker run --rm \
--read-only \
--cap-drop=ALL \
--security-opt=no-new-privileges \
--memory=512m \
--cpus=1.5 \
--pids-limit=200 \
example/app:dev
Add a writable tmpfs or volume only where the process needs it. Test limits under realistic load; resource controls that are lower than startup or peak needs cause restarts and throttling.
Prefer rootless Docker where compatible, but still apply least privilege inside containers. Avoid mounting /var/run/docker.sock; access commonly grants host-equivalent control.
Debugging¶
docker logs --since=10m <container>
docker stats <container>
docker top <container>
docker inspect --format '{{json .State}}' <container>
docker diff <container>
docker image history example/app:dev
Check the exit code, out-of-memory state, health, mounts, configuration, network membership, and emitted logs. Minimal images may lack a shell; use platform-supported debug tooling or reproduce in a diagnostic image instead of expanding the production image permanently.
Image Supply Chain¶
- use trusted, maintained bases and pin deployment by digest;
- generate a software bill of materials where required;
- scan dependencies and images, then prioritize reachable and exploitable risk;
- sign or attest artifacts in a verified build pipeline;
- rebuild rather than patch running containers;
- enforce registry retention without deleting deployed content.
Checklist¶
- Is the build context minimal and secret-free?
- Does a multi-stage build exclude unnecessary build tooling?
- Does the process run as a non-root user and receive signals?
- Are mutable and durable paths explicit?
- Are ports, mounts, capabilities, and resources minimal?
- Is the deployed image identified immutably and reproducible?
- Can the container be diagnosed without manual mutation?