7 Packaging software in images
This chapter explains how to package software into Docker images with an emphasis on manual image construction and the implications of image design. It introduces a simple workflow—start a container from a suitable base, make changes, and commit those changes into a new image—then walks through practical examples from a Hello World tweak to packaging Git. Along the way it highlights that images capture not only filesystem changes but also execution context metadata such as environment variables, working directory, exposed ports, volumes, entrypoint, and default command, all of which shape how users interact with the resulting image.
Digging deeper, the chapter details how union file systems and layers work: additions, modifications, and deletions are recorded on the top writable layer and revealed via docker container diff, while copy‑on‑write explains why changing existing files can inflate size and impact performance. It clarifies the relationship between layers, images, repositories, and tags, showing how commits produce identifiable layers that form images and how tags offer human-friendly references. From these mechanics flow design practices: favor small, cacheable layers; understand that “removing” files typically adds whiteouts without shrinking lower layers; inspect history to locate size culprits; and branch from a clean base rather than undoing prior work, since modern storage drivers remove most practical layer-count limits but not the costs of unnecessary data.
The chapter also covers exporting and importing flat filesystems to build minimal images, including using scratch and streaming tar archives, illustrated by a tiny statically linked Go example. It weighs trade-offs: flat images can be very small and simple but lose history and layer reuse, which may matter with larger stacks. Finally, it presents pragmatic versioning and tagging guidance: maintain multiple coexisting tags (e.g., major, minor, and patch levels) so adopters can control drift; ensure latest points to the latest stable, not just the newest build; and encode meaningful platform or base-image variants in tags because an image’s contract includes its packaged dependencies.
Building an image from a container
A simple file write example on a union file system from two perspectives
Reading files that are located on different layers
Various file addition, change, and deletion combinations over a three-layered image
An image is the collection of layers produced by traversing the parent graph from a top layer.
A visual representation of repositories
Two different tagging schemes (left and right) for the same repository with three images. Dotted lines represent old relationships between a tag and an image.
Summary
This is the first chapter to cover the creation of Docker images, tag management, and other distribution concerns such as image size. Learning this material will help you build images and become a better consumer of images. The following are the key points in the chapter:
- New images are created when changes to a container are committed using the docker container commit command.
- When a container is committed, the configuration it was started with will be encoded into the configuration for the resulting image.
- An image is a stack of layers that’s identified by its top layer.
- An image’s size on disk is the sum of the sizes of its component layers.
- Images can be exported to and imported from a flat tarball representation using the docker container export and docker image import commands.
- The docker image tag command can be used to assign several tags to a single repository.
- Repository maintainers should keep pragmatic tags to ease user adoption and migration control.
- Tag your latest stable build with the latest tag.
- Provide fine-grained and overlapping tags so that adopters have control of the scope of their dependency version creep.
Docker in Action, Second Edition ebook for free