A container image is an immutable, read-only package that contains a filesystem with an application, its libraries and runtime, together with metadata describing how to run it: the default command, environment variables, exposed ports and the user. In cloud computing, and specifically in the field of containers, the image is the build artifact, while a container is a running instance created from that image.
Most images follow the OCI image specification of the Open Container Initiative, which grew out of the Docker image format. An image built with Docker, Buildah or another compliant tool can therefore be stored in any compatible container registry and run by any compliant runtime, which makes the image the portable unit of delivery in cloud-native systems.
How a container image is structured
An image consists of an ordered stack of filesystem layers, a configuration object and a manifest that lists them. Each layer is a compressed archive of file changes: files added, modified or marked as deleted relative to the layer below. At run time, the container engine stacks these layers with a union filesystem such as OverlayFS and adds a thin writable layer on top for the container.
Every component is identified by a content digest, a SHA-256 hash of its bytes. Because identity is derived from content, two images that share a base layer store it only once, and a reference by digest is guaranteed to retrieve exactly the same bits every time.
| Component | Content | Purpose |
|---|---|---|
| Layers | Compressed archives of file changes | Build the root filesystem |
| Image configuration | Command, environment, user, ports, history | Tells the runtime how to start the container |
| Manifest | Digests and sizes of layers and configuration | Describes one image for one platform |
| Image index | List of manifests per architecture | Enables multi-platform images |
| Tag | Human-readable name pointing to a manifest | Convenient but mutable reference |
How images are built and distributed
Images are usually built from a Dockerfile or a similar recipe. Each instruction that changes files, such as copying source code or installing packages, produces a new layer, and the builder caches layers so that unchanged steps are reused. Ordering instructions from least to most frequently changing keeps rebuilds fast.
Multi-stage builds separate the build environment from the final image. Compilers and build tools live in an early stage, and only the resulting binaries or files are copied into a small runtime stage. Finished images are pushed to a container registry, from which hosts pull only the layers they do not already have.
| Base image type | Typical compressed size | Trade-off |
|---|---|---|
| Full distribution | Roughly 30 to 80 MB | Familiar tools, larger attack surface |
| Slim variant | Roughly 20 to 40 MB | Fewer packages, still a shell and package manager |
| Alpine-based | A few megabytes | Very small, uses musl instead of glibc |
| Distroless or scratch | From zero to a few megabytes | Minimal surface, harder to debug |
Container image compared with related concepts
The image sits in the middle of the container workflow, between the recipe that defines it and the running process created from it. Clear boundaries help explain where a change must be made.
| Concept | What it is | Relation to the image |
|---|---|---|
| Dockerfile | Build instructions | Produces the image |
| Container | Running instance with a writable layer | Created from the image |
| Container registry | Storage and distribution service | Holds and serves images |
| Docker Compose | Multi-container definition | References images by name |
| VM image | Full disk with its own kernel | Heavier; container images share the host kernel |
Because a container image does not include a kernel, it relies on the host kernel. A Linux image runs on a Linux kernel, and the image’s CPU architecture must match the host unless emulation is used, which is why multi-architecture images for amd64 and arm64 have become standard.
Building good images in practice
A good image is small, reproducible, secure and free of secrets. These qualities reduce pull time, attack surface and the risk of leaking credentials, and they make deployments predictable across environments.
| Practice | Effect | Common mistake it prevents |
|---|---|---|
| Multi-stage builds | Removes build tools from the final image | Shipping compilers to production |
| Pinning by digest | Deploys exactly the tested image | A tag silently pointing to new content |
| Non-root user | Limits damage after a compromise | Containers running as root by default |
| Secrets at run time | Keeps credentials out of layers | Keys recoverable from image history |
| Regular rebuilds and scans | Picks up security patches | Outdated libraries with known flaws |
- Use a .dockerignore file so local files, logs and credentials never enter the build context.
- Combine package installation and cache cleanup in one layer, because files deleted in a later layer still occupy space.
- Label images with version and source revision to support traceability.
Pitfalls and misconceptions
A frequent misconception is that the latest tag means the newest image. It is only a default name that can point to anything, so production deployments should reference a specific version or digest. Another is that deleting a file in a later instruction removes it from the image; it only hides it, and the data remains in the earlier layer.
Images also age without changing. An image built a year ago may contain dozens of known vulnerabilities in its base packages even though its own code is unchanged, so a regular rebuild and redeploy cycle belongs to normal operations.
The short version
A container image is like a sealed meal kit. It contains every ingredient and the exact recipe needed to make one dish, packed so that it comes out the same in any kitchen.
The kit itself never changes. Each time someone cooks from it, they get a fresh meal, which is the running container. Build once, run the same way everywhere is the whole idea, and it removes the old problem of software working on one computer but not on another.
Where it makes a difference
A development team ships a web API written in Go. Their first image was built on a full distribution base and weighed about 900 MB, including the compiler, test tools and a copy of the local configuration file. Deployments to new servers took several minutes, mostly spent pulling the image.
The team switched to a multi-stage build: the first stage compiles the program, and the final stage copies only the binary and certificates into a distroless base, running as a non-root user. The new image is about 25 MB, starts in seconds, and contains no shell or package manager. Production now references images by digest, and a weekly pipeline rebuilds them so that base image security fixes reach production without manual work.