The biggest surprise about container images is that they’re not monolithic files; they’re composable layers, and you’re often just moving pointers to those layers around.
Let’s see this in action. First, we’ll pull an image. The docker.io/library/ubuntu:22.04 image is a good, common example.
podman pull docker.io/library/ubuntu:22.04
You’ll see output like this, showing individual layers being downloaded:
Resolved "docker.io/library/ubuntu" as an alias for "docker.io/library/ubuntu"
Trying to pull docker.io/library/ubuntu:22.04...
Getting image source signatures
Copying blob sha256:f6396e2d46f7b688a98424c89a813576157686622a9563c6e21695f3b4986880 100.00MiB/100.00MiB [==================================] 0s
Copying blob sha256:d93610e067183d35e14c852c8ff6293730b47008569488f1b87e1b1a390f2e7d 50.00MiB/50.00MiB [==================================] 0s
...
Copying config sha256:5638f92647741510e2147e6070e18f609a36763678586b16028245e2d537a593 2.00KiB/2.00KiB [==================================] 0s
Writing manifest to your-registry:5000/ubuntu:22.04
Notice the Copying blob sha256:... lines. Each of those is a distinct layer. When you pull an image, Podman (or Docker) checks if it already has those specific layers locally. If it does, it skips downloading them. This is the core of image efficiency: sharing layers between different images.
Now, let’s tag that image. Tagging doesn’t create a new image from scratch; it just creates a new reference (a new name and potentially a new repository) that points to the exact same set of layers.
podman tag docker.io/library/ubuntu:22.04 myregistry.local:5000/myubuntu:latest
If you look at podman images, you’ll see both entries, but they share the same IMAGE ID, indicating they are composed of identical layers.
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/library/ubuntu latest 5638f9264774 2 weeks ago 77.7 MB
myregistry.local:5000/myubuntu latest 5638f9264774 2 weeks ago 77.7 MB
The IMAGE ID is the critical part here. It’s a digest of the image’s configuration and all its layers. If the ID is the same, the layers are the same.
Finally, we push this tagged image to a registry.
podman push myregistry.local:5000/myubuntu:latest
This command uploads the layers that aren’t already present in myregistry.local:5000/myubuntu:latest on the remote registry, and then updates the remote registry’s manifest to point to those layers under the name myregistry.local:5000/myubuntu:latest. If you had previously pushed the same set of layers, Podman would detect that and upload nothing, just updating the manifest pointer.
The problem this system solves is efficient distribution and storage of container images. Instead of sending entire, large files over the network, you’re sending only the new or changed layers. Registries can also deduplicate layers, meaning if two different images share a layer, the registry only stores that layer once. This significantly reduces storage costs and network bandwidth.
The mechanics of tagging are that it creates a new entry in your local image store’s index. This index is a mapping of (repository, tag) to an image ID. Pushing then involves communicating with the registry’s API: first, to see which image layers (identified by their digests) are already known by the registry for the target repository and tag, and then to upload any missing layers. Finally, it updates the registry’s manifest for that repository and tag to reference the complete set of layer digests.
What most people don’t realize is that the TAG is not an intrinsic property of the image’s layers themselves. It’s a human-readable alias assigned in a registry’s index. You can have multiple tags pointing to the same image ID (like latest and v1.0.0 both pointing to the same build), or different image IDs (different layers) with the same tag in different registries. The IMAGE ID is the true identifier for the image content.
The next concept you’ll encounter is image signing and verification, which adds a layer of trust to these distributed image artifacts.