{"dataset":{"id":"00000000-0000-0000-0000-000000000011","slug":"docker-best-practices","name":"Docker best practices","description":"Source-linked guidance on Dockerfile and image fundamentals: multi-stage builds, build context, layer caching, non-root users, and base image hygiene. Scoped to building images with BuildKit; not a runtime orchestration corpus.","visibility":"public","version":1,"documentCount":5,"publisher":"Koskamo editorial","license":"Original summaries; linked sources retain their own terms.","reviewedAt":"2026-10-04T00:00:00.000Z","readOnly":true,"createdAt":"2026-10-04T00:00:00.000Z","updatedAt":"2026-10-04T00:00:00.000Z"},"total":5,"documents":[{"title":"Choose minimal base images and pin versions","sourceUrl":"https://docs.docker.com/build/building/best-practices/","text":"Choose a base image built from a trusted source and keep it small: Docker Official Images are a curated collection with clear documentation that are regularly updated, and badges on Docker Hub also mark Verified Publisher and Docker-Sponsored Open Source images. A minimal base image that matches your requirements offers portability and fast downloads, shrinks the size of your image, and minimizes the number of vulnerabilities introduced through the dependencies; consider two types of base image — one for building and unit testing, and another typically slimmer image for production — since a production image often doesn't require build tools such as compilers, build systems, and debugging tools. Images are immutable snapshots, so rebuild your images regularly with updated dependencies, and use the --pull flag to force Docker to check for and download a newer base image even when a version is cached locally. Image tags are mutable: FROM alpine:3.21 resolves to the latest patch version of 3.21, and the same tag can point to a different version months later, which can introduce breaking changes and leaves no audit trail of exact image versions. To secure supply-chain integrity you can pin the base image to a specific digest (FROM alpine:3.21@sha256:...), which guarantees the same image even if the publisher replaces the tag; the trade-off is that digest pinning is tedious to maintain and opts you out of automated security fixes. Scope: base image selection in Dockerfiles. Reviewed October 4, 2026.","id":"docker-best-practices:Choose minimal base images and pin versions","version":1,"createdAt":"2026-10-04T00:00:00.000Z","updatedAt":"2026-10-04T00:00:00.000Z"},{"title":"Run containers as a non-root user with USER","sourceUrl":"https://docs.docker.com/build/building/best-practices/","text":"If a service can run without privileges, use the USER instruction to change to a non-root user, creating the user and group in the Dockerfile first, for example RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres. Assign an explicit UID/GID when it's critical, because users and groups in an image are assigned a non-deterministic UID/GID — the next value is assigned regardless of image rebuilds. Avoid installing or using sudo, since it has unpredictable TTY and signal-forwarding behavior; if you absolutely need similar functionality, such as initializing the daemon as root but running it as non-root, consider gosu. To reduce layers and complexity, avoid switching USER back and forth frequently. Koskamo editorial guidance: run production containers as non-root by default — a compromised or misbehaving process then holds only the container user's privileges rather than root, which shrinks the blast radius of a container breakout or remote code execution. Scope: Dockerfile USER instruction. Reviewed October 4, 2026.","id":"docker-best-practices:Run containers as a non-root user with USER","version":1,"createdAt":"2026-10-04T00:00:00.000Z","updatedAt":"2026-10-04T00:00:00.000Z"},{"title":"Layer caching and instruction ordering","sourceUrl":"https://docs.docker.com/build/cache/optimize/","text":"A layer is reused from the build cache if the instruction and the files it depends on haven't changed since it was previously built, and because a change causes a rebuild for the steps that follow, put expensive steps near the beginning of the Dockerfile and steps that change often near the end to avoid triggering rebuilds of layers that haven't changed. The canonical application is dependency manifests: copy the package management files (for example package.json and yarn.lock) first, run the dependency install, and only then COPY the project source code, so editing application code doesn't reinstall all dependencies. Keeping the build context small with a .dockerignore file also reduces the likelihood of cache invalidation. RUN --mount=type=bind mounts files into the build container for the duration of one instruction without persisting them in the final image or the build cache — bind mounts are read-only by default and the build output must be written outside the mount point — which avoids adding unnecessary layers to the cache when the context is only needed to generate an artifact. RUN --mount=type=cache specifies a persistent package cache (for example /root/.npm) that is shared across builds, so even if you rebuild a layer you only download new or changed packages. For CI pipelines where builders are ephemeral, an external cache shared between builds via the --cache-to and --cache-from options of docker buildx build can drastically speed up builds, for example by pushing cache layers to a registry image. Scope: BuildKit builds. Reviewed October 4, 2026.","id":"docker-best-practices:Layer caching and instruction ordering","version":1,"createdAt":"2026-10-04T00:00:00.000Z","updatedAt":"2026-10-04T00:00:00.000Z"},{"title":"Build context and .dockerignore","sourceUrl":"https://docs.docker.com/build/concepts/context/","text":"The build context is the set of files that a build can access; the positional argument to docker build or docker buildx build specifies it, and it can be a local directory, the URL of a Git repository or tarball, or a plain-text file piped through standard input, which the builder interprets as a Dockerfile with no filesystem context. A local directory context is processed recursively, so all subdirectories are included, and instructions such as COPY and ADD can refer to any file in the context. A .dockerignore file in the root directory of the context excludes files and directories before they are sent to the builder, avoiding unwanted files and improving build speed, especially when using a remote builder; its syntax is a newline-separated list of glob patterns similar to .gitignore files, ** matches any number of directories, lines starting with # are comments, and lines prefixed with ! create exceptions where the last matching line decides whether a file is included or excluded. A Dockerfile-specific ignore file named <Dockerfile>.dockerignore placed next to the Dockerfile takes precedence over the .dockerignore at the root of the context when both exist. You can exclude the Dockerfile and .dockerignore themselves, but they are still sent to the builder because they're needed for the build, and you can't copy them into the image with ADD, COPY, or bind mounts. Koskamo editorial guidance: always exclude secrets, the .git directory, and dependency directories such as node_modules from the context so credentials and bloat never reach the builder or the image. Scope: BuildKit build contexts. Reviewed October 4, 2026.","id":"docker-best-practices:Build context and .dockerignore","version":1,"createdAt":"2026-10-04T00:00:00.000Z","updatedAt":"2026-10-04T00:00:00.000Z"},{"title":"Multi-stage builds keep images small","sourceUrl":"https://docs.docker.com/build/building/multi-stage/","text":"Multi-stage builds use multiple FROM statements in a single Dockerfile; each FROM can use a different base and begins a new stage of the build. You selectively copy artifacts from one stage into another with COPY --from, leaving behind everything you don't want in the final image, so the end result can be a tiny production image containing only the built binary with none of the build tools required to build it. Name stages with FROM <image> AS <name> and refer to the name in COPY --from so the copy doesn't break if instructions are reordered later; unnamed stages are referenced by integer index starting at 0 for the first FROM. You can stop at a specific build stage with docker build --target <name>, useful for keeping a debug stage with debugging symbols or tools alongside a lean production stage, and BuildKit only builds the stages that the target stage depends on. COPY --from can also copy artifacts from a separate image by local image name, a tag available locally or on a registry, or a tag ID, pulling the image if necessary. Scope: Dockerfile builds with BuildKit. Reviewed October 4, 2026.","id":"docker-best-practices:Multi-stage builds keep images small","version":1,"createdAt":"2026-10-04T00:00:00.000Z","updatedAt":"2026-10-04T00:00:00.000Z"}]}