Dockerfile Best Practices: Secure, Efficient Container Images

Dockerfile Best Practices#

A Dockerfile is a security boundary. Every decision – base image, installed package, file copied in, user the process runs as – determines the attack surface of your running container. Most Dockerfiles in the wild are bloated, run as root, and ship debug tools an attacker can use. Here is how to fix that.

Choose the Right Base Image#

Your base image choice is the single biggest factor in image size and vulnerability count.

Container Build Optimization: BuildKit, Layer Caching, Multi-Stage, and Build Performance

Container Build Optimization#

A container build that takes eight minutes in CI is not just slow – it compounds across every push, every developer, every day. The difference between a naive Dockerfile and an optimized one is often the difference between a two-minute build and a twelve-minute build. The techniques here are not theoretical. They are the specific changes that eliminate wasted time.

BuildKit Over Legacy Builder#

BuildKit is the modern Docker build engine and the default since Docker 23.0. If you are running an older version, enable it explicitly with DOCKER_BUILDKIT=1. BuildKit provides several capabilities the legacy builder lacks.

kubectl debug and Ephemeral Containers: Non-Invasive Production Debugging

kubectl debug and Ephemeral Containers#

Production containers should be minimal. Distroless images, scratch-based Go binaries, and hardened base images strip out shells, package managers, and debugging tools. This is good for security and image size, but it means kubectl exec gives you nothing to work with. Ephemeral containers solve this problem.

The Problem#

A typical distroless container has no shell:

$ kubectl exec -it payments-api-7f8b9c6d4-x2k9m -- /bin/sh
OCI runtime exec failed: exec failed: unable to start container process:
exec: "/bin/sh": stat /bin/sh: no such file or directory

You cannot install tools, you cannot inspect files, and you cannot run any diagnostic commands. The application is returning 500 errors and you have nothing but logs.