The problem with Docker and Alpine's package pinning

Being a huge fan of Alpine Linux, I'm using it in a lot of my built Docker images. The resulting images are very small and minimal, so ideal for containerized environments.

Recently I wanted to build Docker images for a dated project. But Alpine's package manager apk failed, and the reason surprised me.

What's one of the biggest benefits of Docker? Clearly reproducibility: It doesn't matter where you run your images, or when you run them: The result will always be the same.

Reproducibility isn't only important at runtime, though. It's also very important when building the images: Again, no matter where or when you build them: Same result!

Version pinning

For achieving consistent builds, the dependencies you rely on must be pinned down to a specific version. You can't just go install nodejs, you have to be very specific, like install nodejs@8.10.0.

Why is that? If you don't pin down version numbers, your images are dependent on the point of time when they are built. When package maintainers decide to release a new version, it will be automatically installed as soon as you rebuild your image the next time.

Alpine Linux and version pinning

Alpine Linux does support two ways of pinning down packages: Repository and package pinning.

Alpine Linux itself carries a version number (current version as of writing is 3.7). Every Alpine Linux version has its own package repository (the thing where the package archives are stored).

With repository pinning, you can actually pin down the packages to the latest available package version of the selected Alpine Linux version. For example, in Alpine 3.5, the package Node.js might be 2.0, and in Alpine 3.4 it's 1.9. By pinning down the repository to Alpine 3.4, you will alwaysget Node.js 1.9, because Alpine 3.4 is an old version and not updated anymore.

With package pinning, you can pin down the packages to their respective versions. It lets you specify what version of a package you want, like Node.js in version 1.2.3. This sounds perfect!

Alpine doesn't keep old packages

Unfortunately Alpine Linux does not keep old packages. When I tried building the dated project, I got this response from apk:

ERROR: unsatisfiable constraints:
    breaks: world[postgresql-dev=10.2-r0]

The postgresql-dev version I used to use (10.2-r0) isn't available anymore. Instead 10.3-r0 has been released, and the old package has been dropped from the repository.

This is a huge problem, as it forces you to avoid pinning down package versions and resort to repository pinning.

However, when rebuilding your images, the packages might be installed in versions you didn't expect. And that can be a real problem, depending on what changed in the respective packages when they were updated.

PyPI, npm, ...?

I'd like to have it similar to PyPI and npm: No version is dropped, so version pinning works perfectly fine, no matter when you build or use your stuff.

Alpine is a great distribution, especially for Docker. Of course I will continue using it, but you better pay a good amount of attention to package versions in the future and install unit tests to get you covered!

This article was published on March 23rd, 2018 by stschindler and is tagged with