← All lessons
Docker
All Docker snippets · 186
Every snippet has its own focus page with an explanation and the output it produces.
Dockerfile Basics · 14
- Minimal imageEvery Dockerfile starts with FROM, which sets the base image the new layers are stacked on.
- Untagged base imageOmitting the tag implies :latest, so the same Dockerfile can build against a different runtime tomorrow.
- Running a build commandRUN executes a command at build time and commits the result as a new image layer.
- Split RUN loses the cacheA cached update layer can pair with stale package indexes, so update and install belong in one RUN.
- Setting the working directoryWORKDIR sets the directory for later RUN, CMD, COPY and ENTRYPOINT instructions and creates it if missing.
- cd does not persistEach RUN starts a fresh shell, so a cd in one instruction has no effect on the next; use WORKDIR.
- Default container commandCMD supplies the default command, which is replaced by any command passed to docker run.
- Copying project filesCOPY moves files from the build context into the image at the given path.
- Documenting a portEXPOSE documents the port the service listens on; it does not publish it to the host.
- EXPOSE does not publishWithout -p on docker run the port stays inside the container network, EXPOSE is metadata only.
- Full Dockerfile skeletonThis is the canonical shape of an application Dockerfile: base, workdir, dependencies, source, command.
- Building and taggingThe build context is the final argument, and -t names the resulting image.
- Alternate Dockerfile path-f picks the Dockerfile while the trailing dot still defines the build context root.
- Context outside the DockerfileCOPY cannot escape the build context, so parent-directory paths always fail.
Base Images · 8
- Slim over full imagesThe slim variants drop build tooling and docs, cutting image size by hundreds of megabytes.
- Alpine uses muslAlpine ships musl instead of glibc, so many wheels have no prebuilt binary and must compile from source.
- Distroless runtimeDistroless images contain no shell or package manager, shrinking the attack surface of the runtime.
- No shell to exec intoDistroless images have no /bin/sh, so debugging needs a sidecar or a debug-tagged variant.
- Building on scratchscratch is an empty base, valid only for fully static binaries with no runtime dependencies.
- Missing CA certificatesscratch has no CA bundle, so any outbound HTTPS call from the binary fails until certs are copied in.
- Pinning by digestA digest pins the exact image content, making builds reproducible even if the tag is republished.
- Wrong architecture imagePulling an image built for another architecture only works under emulation and is dramatically slower.
Layers & Caching · 9
- Dependencies before sourceCopying manifests first keeps the expensive install layer cached when only source files change.
- COPY . . invalidates everythingCopying the whole tree before installing busts the install cache on every single source edit.
- Chaining to one layerChained commands with cleanup in the same RUN keep the deleted files out of the final layer.
- Deleting in a later layerLayers are additive, so files removed in a later instruction still occupy space in the earlier one.
- BuildKit cache mountA cache mount persists the package cache across builds without baking it into the image.
- Go module cacheModule downloads are reused between builds while staying out of the image layers.
- Inspecting layer sizesdocker history attributes size to each instruction, showing which step inflated the image.
- Forcing a fresh layerChanging a build arg invalidates that layer and every layer after it on demand.
- Building without cache--no-cache re-executes every instruction, useful when an upstream package index changed.
COPY & ADD · 9
- Copy to a directoryA destination ending in a slash is treated as a directory and the file keeps its name.
- Copying a directory's contentsCOPY of a directory copies its contents, not the directory itself, so /app/src never exists.
- Copy with ownership--chown sets ownership during the copy, avoiding a second layer that duplicates every file.
- Copy from another image--from can pull files straight out of another image without a build stage of its own.
- ADD unpacks archivesADD auto-extracts local tar archives, which is the one behaviour COPY does not have.
- ADD with a remote URLRemote ADD does not extract, cannot be checksum-verified inline, and bakes the download into a layer.
- Copying with a wildcardThe wildcard matches package.json and package-lock.json in a single instruction.
- Excluding build noiseA .dockerignore file keeps large or secret paths out of the build context entirely.
- Secrets in the contextWithout .dockerignore, .env and .git land in the image and stay readable in the layer history.
ENV & ARG · 10
- Setting an environment variableENV values persist into the running container and are visible to every process it starts.
- Multiple values in one layerGrouping ENV assignments into one instruction keeps the layer count down.
- Build-time argumentARG values exist only during the build and can be overridden with --build-arg.
- ARG is gone at runtimeBuild args are not environment variables in the container, so the value is empty at run time.
- ARG before FROM has limited scopeAn ARG declared before FROM is only usable in FROM lines unless it is redeclared inside the stage.
- Parameterised base imageRedeclaring the ARG inside the stage brings the global value back into scope.
- Secrets leak through build argsBuild args are recorded in the image history, so anyone with the image can read the value.
- BuildKit secret mountA secret mount exposes the file only for that instruction and never writes it to a layer.
- Overriding at run timeValues passed with -e override any ENV baked into the image.
- Loading an env file--env-file reads KEY=VALUE pairs from disk, keeping secrets out of shell history.
CMD & ENTRYPOINT · 8
- Exec form vs shell formENTRYPOINT fixes the executable while CMD supplies default arguments a user can replace.
- Shell form swallows signalsShell form runs the process under /bin/sh as PID 1, so SIGTERM never reaches the application.
- Overriding CMD at run timeA command after the image name replaces CMD entirely for that container.
- Overriding ENTRYPOINT--entrypoint replaces the baked-in entrypoint, which is the usual way into a misbehaving image.
- CMD ignored when args are passedAny runtime argument replaces CMD wholesale, so the default only appears when none is given.
- Entrypoint wrapper with execA wrapper script can do setup then exec "$@" so the real process inherits PID 1.
- Forgetting exec in the wrapperWithout exec the wrapper stays PID 1 and shutdown signals never reach the real process.
- Reaping zombies with --init--init inserts a tiny init process as PID 1 that forwards signals and reaps orphaned children.
Multi-stage Builds · 8
- Compile then shipThe toolchain stays in the builder stage while only the compiled binary reaches the final image.
- Node build and runtime splitDev dependencies and source stay behind, shipping only the build output and runtime modules.
- Shared base stageStages can inherit from earlier named stages, keeping shared setup in one place.
- Building a specific stage--target stops the build at a named stage, which is handy for a test or debug image.
- One file, two targetsSeparate targets let development and production share a base without duplicating the Dockerfile.
- Unreferenced stages are skippedBuildKit only builds stages the target depends on, so nothing from an unused stage is present.
- Tests as a build stageRunning tests in a stage makes a failing suite fail the build itself.
- Copying from a numbered stageStages can be referenced by index, though names are far more readable in practice.
Users & Security · 9
- Dropping rootSwitching to an unprivileged user limits what a compromised process can reach.
- Writing to a root-owned pathFiles copied before USER stay owned by root, so the unprivileged user cannot write there.
- Creating a service accountCreating a dedicated account gives the service a home directory and a stable uid.
- Read-only root filesystemA read-only rootfs blocks tampering at runtime, with a tmpfs for the paths that must be writable.
- Dropping capabilitiesDropping all capabilities and re-adding only what is needed follows least privilege.
- Privileged containersA privileged container gets nearly all host capabilities, effectively removing the isolation boundary.
- Blocking privilege escalationno-new-privileges prevents setuid binaries from raising the process's privileges.
- Scanning an imageScanning surfaces known CVEs in the base image and dependencies before the image ships.
- Containers run as root by defaultWithout a USER instruction the process runs as uid 0, which maps to root on the host in many setups.
Healthcheck & Signals · 7
- HTTP health probeThe exit status of the probe decides health, and repeated failures mark the container unhealthy.
- Start period for slow bootsstart-period gives a slow-starting app time to boot before failures count against it.
- Probe tool not installedThe probe runs inside the container, so a missing curl makes it fail permanently.
- Custom stop signalnginx drains connections on SIGQUIT, so STOPSIGNAL makes docker stop graceful.
- Extending the grace periodDocker sends SIGTERM, waits the timeout, then SIGKILLs whatever is still running.
- PID 1 ignores SIGTERMShell-form CMD makes /bin/sh PID 1, and sh does not forward SIGTERM to its child.
- Restart policyunless-stopped restarts on failure and after daemon restarts, but respects a manual stop.
Volumes & Storage · 10
- Persisting database dataA named volume outlives the container, so data survives a recreate or upgrade.
- Live-reloading sourceA bind mount maps a host directory into the container so edits appear immediately.
- Mount hides image contentsA bind mount shadows whatever the image had at that path, including installed dependencies.
- Protecting node_modulesAn anonymous volume on the nested path keeps the image's node_modules visible under a bind mount.
- VOLUME freezes later writesChanges written to a declared volume path after the VOLUME instruction are discarded.
- In-memory scratch spaceA tmpfs mount lives in memory and never touches disk, which suits ephemeral secrets and caches.
- Mounting config read-onlyThe :ro flag stops the container from modifying mounted host files.
- Finding a volume on diskVolume metadata reveals where the daemon stores the data on the host.
- Dangling volumes accumulateRemoving a container does not remove its anonymous volumes, so disk usage creeps up.
- Backing up a volumeMounting both the volume and a host directory into a throwaway container makes backups simple.
Networking · 10
- Publishing a portThe -p flag maps host port to container port in host:container order.
- Reversed port mappingThe container side comes second, so mapping to a port nginx does not listen on yields nothing.
- Service discovery by nameOn a user-defined bridge, Docker's embedded DNS resolves container names to addresses.
- No DNS on the default bridgeThe legacy default bridge has no automatic name resolution; a user-defined network does.
- localhost inside a containerlocalhost refers to the container itself, not the host or a sibling container.
- Reaching a service on the hosthost-gateway resolves to the host address so containers can reach services running outside Docker.
- Sharing the host network stackHost networking skips the NAT layer entirely, which removes isolation but also port mapping.
- Publishing on loopback onlyPrefixing the host address limits exposure to the local machine instead of every interface.
- Listing container addressesNetwork inspection shows every attached container and the address assigned to it.
- Binding to 127.0.0.1 in-containerA server bound to loopback inside the container is unreachable even with -p published.
CLI: Containers · 12
- One-off container--rm deletes the container as soon as it exits, keeping the machine clean.
- Interactive shell-it attaches a terminal and keeps stdin open, which is what a shell needs.
- Listing containersFormat strings turn the container list into exactly the columns you care about.
- Shell into a running containerexec starts an extra process in an existing container without restarting it.
- Following logsFollowing from the last lines avoids replaying the whole history of a long-running service.
- Extracting one fieldGo templates pull a single value out of the large inspect document.
- Copying files outdocker cp moves files in either direction between host and container.
- Live resource usage--no-stream prints one snapshot instead of a live-updating table, which scripts better.
- Removing a running containerA running container must be stopped first, or removed with -f.
- Committing a running containerCommitting captures manual changes but produces an image nobody can rebuild from source.
- Waiting on an exit codewait blocks until the container exits and prints its status code, which suits CI scripts.
- Changing limits liveSome resource constraints can be adjusted without recreating the container.
CLI: Images · 8
- Tagging an imageA tag is just another name pointing at the same image id.
- Pushing to a registryThe image name must include the registry host for the push to go anywhere but Docker Hub.
- Pulling a specific tagPulling an explicit tag avoids surprises from a moving latest.
- Sorting images by sizeFormatting plus sort quickly identifies which images are eating the most disk.
- Exporting an image to a tarballsave and load move images between machines without a registry.
- Reclaiming disk spacesystem df shows what is using space before prune removes unused images, networks and volumes.
- Prune removes more than expectedWith -a every image not used by a running container goes, including ones you meant to keep.
- Image still in useAn image backing an existing container cannot be removed until the container is gone.
Compose Basics · 15
- Single serviceA compose file describes services declaratively instead of as a chain of docker run flags.
- Building from a DockerfileA build key makes compose build the image from the local context before starting the service.
- Starting the stack--build forces a rebuild so code changes are actually picked up on start.
- down removes the networkPlain down keeps named volumes; -v deletes them along with the data they hold.
- depends_on does not wait for readinessdepends_on only orders startup; the database may still be initialising when the API connects.
- Waiting on a healthcheckservice_healthy holds the dependent service until the probe reports healthy.
- Environment from a fileInline environment values take precedence over the ones loaded from env_file.
- Named volume in composeTop-level volumes declares the volume; the service mounts it by name.
- Custom networksKeeping the database off the frontend network stops it being reachable from public-facing services.
- Running multiple replicasScaling works for stateless services that do not publish a fixed host port.
- Scaling with a fixed portOnly one container can own a host port, so scaling a service with a fixed mapping fails.
- Following stack logsCompose multiplexes logs from several services with a prefix per service.
- Running a command in a servicecompose exec targets a service by name instead of a container id.
- One-off task containerrun starts a throwaway container from the service definition without touching the running one.
- Rendering the final configconfig prints the merged and interpolated file, which is the fastest way to debug overrides.
Compose Advanced · 11
- Layering override filesLater files are merged over earlier ones, keeping environment differences out of the base file.
- YAML anchors for shared configAn anchor defines the block once and aliases reuse it across services.
- Reusing a service definitionextends copies another service's configuration and then applies local overrides.
- Optional services with profilesProfiled services stay down until their profile is requested with --profile.
- Missing variable becomes emptyAn unset variable interpolates to an empty string and the image reference becomes invalid.
- Default values in composeThe :- syntax supplies a fallback when the variable is unset or empty.
- Compose secretsSecrets are mounted at /run/secrets/<name> instead of being exposed as environment variables.
- Limiting CPU and memoryResource limits stop one noisy service from starving the rest of the stack.
- Restart policy in composeCompose maps restart directly onto the container restart policy.
- Sync on file changecompose watch syncs source into the container and rebuilds only when dependencies change.
- Directory name shapes resource namesVolumes and networks are prefixed with the project name, so renaming the directory orphans them.
BuildKit & Buildx · 8
- Bind mount during buildA bind mount exposes the build context to one instruction without copying it into a layer.
- Building for two architecturesbuildx produces a manifest list so each architecture pulls the right image.
- Using build platform argsBuilding on the native platform and cross-compiling avoids slow QEMU emulation.
- Registry-backed build cacheExporting the cache to a registry lets ephemeral CI runners reuse layers between jobs.
- Declarative buildx bakebake keeps multi-target build configuration in a file instead of long command lines.
- Creating a builder instanceThe container driver unlocks multi-platform builds and advanced cache exporters.
- Exporting build output to diskA local output writes the stage's filesystem to the host rather than producing an image.
- Heredoc in a RUN instructionHeredocs keep multi-line shell readable without trailing backslashes.
Debugging & Ops · 9
- Inspecting a failed layerBuilding up to the failing stage gives you a shell in the exact environment that broke.
- Reading a container's exit codeExit code 137 means SIGKILL, usually the out-of-memory killer.
- Memory limit kills the processExceeding the memory limit gets the process killed by the kernel, not a graceful error.
- Seeing filesystem changesdiff lists files added, changed or deleted relative to the image, which exposes stray writes.
- Watching daemon eventsThe event stream shows exactly when containers died, which is invaluable for crash loops.
- Capping log file growthWithout log rotation a chatty container can fill the host disk.
- Debugging a container without a shellJoining the target's namespaces from a tools image debugs distroless containers safely.
- Container timezone is UTCImages default to UTC regardless of the host timezone unless TZ is set explicitly.
- Breaking down disk usageThe verbose form attributes space to individual images, containers and volumes.
Image Optimisation · 7
- apt cleanup in one layer--no-install-recommends plus index cleanup keeps a Debian base image lean.
- Alpine without a cache--no-cache skips writing the package index, saving a few megabytes and a cleanup step.
- Stripping a Go binaryDropping the symbol table and debug info shrinks the binary substantially.
- Installing production deps onlySkipping dev dependencies removes build tooling that the runtime never needs.
- Layer count is not the whole storyFewer layers does not mean a smaller image; what you copy in is what dominates the size.
- Comparing base image sizesComparing bases side by side makes the cost of a full distro image obvious.
- Auditing wasted spaceLayer auditing highlights files added then deleted, which still cost space in the image.
Registry & Tagging · 6
- Tagging a releaseSeveral tags can point at one image id, giving callers a choice of stability.
- latest is not the newestlatest is an ordinary tag with no special meaning; it points wherever it was last pushed.
- Inspecting a remote manifestManifest inspection reveals which platforms a published tag actually supports.
- Running a registry locallyA local registry is the quickest way to share images across machines in a lab.
- Credentials stored in plain textWithout a credential helper, docker login writes base64-encoded credentials to disk.
- Pulling by digestPulling by digest guarantees the exact bytes regardless of what the tag now points to.
Runtime Configuration · 8
- Capping CPU and memorySetting memory-swap equal to memory disables swap, making OOM behaviour predictable.
- Raising the file descriptor limitConnection-heavy services often need a higher nofile limit than the daemon default.
- Adding image metadataOCI standard labels let registries and tooling link an image back to its source.
- Filtering by labelLabels give you a query language over containers without relying on naming conventions.
- Passing a device through--device grants access to one host device without going fully privileged.
- Setting the hostnameThe container hostname defaults to the short container id unless set explicitly.
- Configuring the timezonetzdata plus a TZ variable makes container timestamps match the expected locale.
- Piping into a container-i keeps stdin open so the container can act as a filter in a shell pipeline.