Tags are annotations that can be applied to Docker images after they’re built. If you look at an image in a repository like Docker Hub or Quay.io you’ll see different versions of that image with different tags. “Great”, you think to yourself, “I can see what version I’m using by looking at the tag”.
Well, sort of. But you can easily get yourself — and other people — into an awful lot of trouble.
Latest is nothing special
It’s quite likely that the first tag you’ll come across is latest. You might pull an official image like Ubuntu like this:
docker pull ubuntu:latest
That sounds a lot like you’re pulling the latest version of Ubuntu, right? Maybe you are. Or maybe you’re not.
Here are just a few of the most recent tags for the Ubuntu image on Docker Hub (this list was generated using our MicroBadger tool).
The image tagged latest is also tagged 16.04 and xenial-20160713, and it’s not the most recently-built image.
Don’t get me wrong, you often do want to pull the image tagged latest — and it’s very likely that the Ubuntu maintainers have put this tag where it is because it’s the version that most people should be using right now. But it’s worth knowing that it’s just an arbitrary tag that people use, purely by convention. Latest is not updated automatically, and if you want an image to have that tag, you have to add it yourself.
If you want to mess people around, you could add the latest tag to some arbitrary old version of your Docker image. Try moving it around from one version to another every now and again. This would be especially effective if you change the code a lot between different versions. You’ll soon have lots of people using your image with absolutely no clue what version of the code they are running.
Major and minor versions
You can give multiple tags to a single image, and it’s quite common to use this to apply labels indicating major and minor version numbers. Here we can see that the Postgres image tagged latest is also tagged with 9, 9.5 and 9.5.3.
Of course version 9 hasn’t always been 9.5.3. We can’t turn back time to check, but it’s probably the case that before 9.5.3 was released, the image currently tagged 9.5.2 was also tagged 9 and 9.5.
A lot of the time you don’t care about the precise version of the code you’re using — and in many cases you want the most recent proven minor release with all the best bug fixes and patches. But it does make it potentially more difficult to unpick in the future.
So you can happily screw people about by building a Docker image based on a major tag number (say, postgres:9 in this example). The version they actually get will depend on whatever the tag 9 is pointing at on the day you built it. Whenever you rebuild the image, the base image could be quite different.
Or you could simply tag the images you build with arbitrary tag values — you know, say this is version 3.0.4 when it’s really 2.7.1. That will have hilarious consequences when it comes to debugging issues in the field, and you’ll be the 24/7 support team’s hero. Not.
Cleaning up the mess with metadata
The reason why tags offer so much opportunity to screw people around is that they can be changed at will. This is in contrast to the image itself, which is immutable.
So wouldn’t it be better to bake the version information into the image itself? That way it would be impossible to change later on. And the good news is you can do this with the Label instruction in the Docker file.
Labels are key-value pairs that get built into the image. You could include version information at build time in a label. We’re very keen on the idea of baking in version control information so that you know exactly which source code version your image was built from.
There’s nothing to stop you from putting incorrect data into labels too, of course — the old adage ‘garbage in, garbage out’ holds as true as ever. But at least once your garbage is stored inside the image, there’s no changing it. If you’re looking to perform a bait-and-switch trick on your colleagues with metadata, you’re better off using tags than labels to do it.