Using Semver for Docker Image Tags

Marc Campbell
5 min readAug 8, 2018

--

Using a semantic versioning tagging scheme ensures that users of a Docker image can guarantee compatibility and stay updated without having to be aware of every new build the image maintainer creates. By subscribing to the major label only, you are guaranteed backward compatibility and get new features and fixes each time you pull. By subscribing to the major.minor label, you are guaranteed backward compatibility and bug fixes, but will have to decide when to implement new features. Finally, by subscribing to the major.minor.patch label, you are guaranteed to always get the exact same image layer, excluding every update.

In order to make this a reality, public Docker images should consider a move to always use semver tag names. While this pattern has been adopted by many of the most popular public Docker images, it still isn’t implemented by all. This post is my attempt at providing a well reasoned argument for why and how they should.

How To Create Semver Tags

When pushing a new version of an image:

  1. Create a new patch tag.
  2. Overwrite the existing minor and major tags.
  3. Always update the latest tag when pushing new versions.
  4. Never overwrite patch tags.

Overview

Semantic Versioning (semver) is a commonly used software versioning system. The spec is easy enough to understand and recommended reading for anyone shipping software. Chances are, you’re familiar with the concepts of semver because it’s an established system that is widely used in Node.js, Rails and other ecosystems.

In short, semantic versioning is the name given to version tags that look like “1.0.2”, where 1 called the major version, 0 the minor version, and 2 is the patch version. There are many details listed in the spec that can be used when creating nightly, beta and other types of releases.

To implement semantic versioning in Docker image tags, multiple tags should be pushed to the registry on every build, overwriting existing tags as needed. It’s important to follow the backward-compatibility rules of the semantic versioning protocol when changing the patch version, minor version or major version. Additionally, the exact same version number (major.minor.patch) should never be overwritten with different content.

Using Semver For Image Tags

Almost every Docker image has more than one tag, but there’s no standard or guarantee that prescribes how an image should be tagged or any standard that can provide information about what each tag references. While there are some popular practices used in some images, there’s no shortage of tagging conventions in Docker Hub today. Some of these methods are better than others to help with discoverability, maintainability and providing an easy way for the image to be used.

Example

A clean and well tagged image using semver is Docker’s registry image. At the time of this writing, the current version of this image is 2.6.2. Referring to the semantic versioning protocol, this means that, right now, the following image tags should point to the exact same layer:

  • registry:2.6.2
  • registry:2.6
  • registry:2
  • registry:latest

When Docker pushes version 2.6.3, they will overwrite all but the 2.6.2 tag. None of this happens automatically in Docker; your build and release process should tag and push each of these separately:

$ docker build -t registry:latest .
$ docker push registry:latest
$ docker tag registry:latest registry:2
$ docker push registry:2
$ docker tag registry:latest registry:2.6
$ docker push registry:2.6
$ docker tag registry:latest registry 2.6.3
$ docker push registry:2.6.3

Why This Matters

External (public) Docker images should use a tagging pattern that’s understood by the audience, compatible with other systems and processes, and provides for some self-documentation and discovery purposes. Images that are private but will be shared with customers or external groups fall into this category also.

A less transparent solution to tagging is to only tagging with the Git SHA. The biggest problem with this is that new users have no way to know which version is current, which is the one I should use. In that example, Docker Hub shows “2 years ago” for every tag. The Readme doesn’t indicate which to use, and there’s no link to a GitHub repo to compare to. This image is not usable to someone not familiar with the code or how to decipher which image tag is newest. If you want to include SHA tags, include human-readable tag names also.

Another option that is sometimes used is to only tag by the date. With this approach, it’s possible to figure out which is the most recent. But it’s missing an indicator of how critical this update is and when it happens. With semantic versioning, users can subscribe to a major, or a minor release and simply get patches automatically without risk of introducing breaking changes.

I don’t even know what would happen if a second image needed to be pushed in a day — I can only assume it will overwrite the first making it really difficult to produce a consistent deployment.

Internal Use Images

Internal use only (private) Docker images should use a tagging pattern that’s both consistent and forward-looking to increase the image’s maintainability over time. Often, internal-only images are managed completely by automation and tightly-coupled systems. In this scenario, using a SHA that references the content of the image is a good idea. This isn’t discoverable, but it’s very useful to map a git commit to an image build. This practice is good for internally used images, but not good when sharing images externally.

Recommendation

We’ve found that the overwhelming majority of popular images use a predictable semver style tagging system. This ensures that the users of the image can subscribe to a tag and release schedule that is compatible with their systems.

Each Docker image should be tagged multiple times when building and pushing to the registry.

1. Tag the image using a semver label, and push all individual version components separately.

2. Tag the image with a tag to indicate that it’s the “latest”. Docker uses latest as the default tag name, but the latest tag is not automatically applied when pushing a new image.

3. For images that will not be shared, tag the image with a tag that will help connect this binary to the source that was used to create it. One of the most common and best practices to achieve this is to simply use the ${GIT_SHA:0:7} of the repo that contains the Dockerfile and the source, after committing the latest change.

--

--

Marc Campbell

Founder, replicated.com. Previously founder of @lookioapp. Developer #golang, #erlang and more.