How to Create a Docker Image: A Complete Guide

Docker images are the foundation of containerized applications. Whether you're packaging a web server, a machine learning pipeline, or a simple script, understanding how Docker images are built — and what shapes that process — is essential for anyone working with containers.

What Is a Docker Image?

A Docker image is a read-only template that contains everything needed to run an application: the operating system layer, runtime environment, dependencies, configuration files, and application code. When you run an image, Docker creates a live instance called a container.

Images are built in layers. Each instruction in a build file adds a new layer on top of the previous one. Docker caches these layers, which means rebuilding an image after a small change only reprocesses the affected layers — not the entire image.

The Core Tool: The Dockerfile

The primary method for creating a Docker image is writing a Dockerfile — a plain-text file containing a sequence of instructions that Docker executes in order.

Essential Dockerfile Instructions

InstructionPurpose
FROMSets the base image (e.g., Ubuntu, Alpine, Python)
RUNExecutes shell commands during the build
COPY / ADDCopies files from your local machine into the image
WORKDIRSets the working directory inside the image
ENVDefines environment variables
EXPOSEDocuments which network port the container listens on
CMD / ENTRYPOINTDefines the default command run when the container starts

A minimal Dockerfile for a Python application might look like this:

FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD ["python", "main.py"] 

This tells Docker to start from an official slim Python image, set up a working directory, install dependencies, copy the application code, and define the startup command.

Building the Image

Once your Dockerfile is written, you build the image using the Docker CLI:

docker build -t my-app:1.0 . 
  • -t assigns a tag (name and version) to the image
  • The . tells Docker to look for the Dockerfile in the current directory

Docker reads each instruction top to bottom, executing them in sequence and committing each result as a new layer. The final output is a complete, self-contained image stored locally.

You can verify it was created with:

docker images 

🧱 Choosing the Right Base Image

The FROM instruction is one of the most consequential decisions in image creation. Your base image determines:

  • Image size — Full OS images like ubuntu:latest can exceed 70MB; minimal images like alpine stay under 10MB
  • Security surface — Smaller base images typically include fewer packages and therefore fewer potential vulnerabilities
  • Compatibility — Some language runtimes or system libraries may not be available on stripped-down bases like Alpine without extra steps

Common base image patterns include:

  • Official language images (python, node, golang) — convenient, well-maintained
  • Slim variants (python:3.11-slim) — reduced footprint, still Debian-based
  • Alpine-based images — very small, but may require additional configuration for certain native dependencies
  • Distroless images — contain only the runtime, no shell or package manager; often used in production for security

The right choice depends on your application's dependencies, your team's familiarity, and your deployment environment's constraints.

Layer Ordering and Build Efficiency

Because Docker caches layers, the order of instructions matters. Instructions that change rarely (like installing system packages) should appear early in the Dockerfile. Instructions that change frequently (like copying your application source code) should appear late.

If you copy your source code before installing dependencies, any code change invalidates the dependency cache — forcing a full reinstall on every build. Reversing the order preserves that cache.

Multi-Stage Builds

For production images, multi-stage builds are a common optimization. The idea: use one stage to compile or build your application, then copy only the finished artifact into a clean, minimal final image — leaving behind build tools, compilers, and other overhead.

# Build stage FROM golang:1.21 AS builder WORKDIR /app COPY . . RUN go build -o myapp # Final stage FROM alpine:latest COPY --from=builder /app/myapp /myapp CMD ["/myapp"] 

This approach can reduce final image size dramatically and limits what ends up in the image that runs in production.

Variables That Affect Your Image-Building Approach

How you build Docker images — and what "best practice" looks like — shifts significantly based on several factors:

  • Application type: Compiled languages (Go, Rust, Java) benefit more from multi-stage builds than interpreted ones (Python, Ruby)
  • Team size and CI/CD pipeline: Automated builds require reproducible, cache-friendly Dockerfiles
  • Deployment target: Kubernetes clusters, cloud container registries, and local development environments all have different size and security tolerances
  • Security requirements: Regulated environments often demand distroless or hardened base images and non-root users inside containers
  • Dependency complexity: Applications with native extensions or system-level libraries may need more control over the base OS

🔍 Other Ways to Create Docker Images

While Dockerfiles are the standard, they're not the only method:

  • docker commit — Saves a running container's current state as a new image. Useful for quick experimentation but produces images that are difficult to reproduce or audit
  • BuildKit — Docker's modern build backend, enabled by default in recent versions, offering parallel builds, secret mounting, and improved caching
  • Buildpacks — Tools like Cloud Native Buildpacks automatically detect your application type and produce an image without a Dockerfile
  • Kaniko / Buildah — Daemonless build tools used in environments where running Docker itself isn't practical (e.g., inside Kubernetes pods)

Each approach trades off simplicity, reproducibility, and control differently.

🗂️ The Missing Piece Is Your Setup

Understanding how Docker images are assembled — layers, base images, build stages, caching — gives you the foundation. But what your image should actually contain, how large it should be, which base image fits your stack, and how much optimization effort is worth it all comes down to your specific application, infrastructure, and deployment workflow. Those details live on your side of the screen.