June 16, 2022

Version Ranges: A Smarter Way to Manage Packages

How to & Use Cases

You can now specify a desired version range to be respected by your packages when managing them with Puppet.

Table of Contents

Why Version Ranges for Puppet?

Over the years, version range support for packages has been one of the oldest and most-requested features in the Puppet backlog (concluding as the 3rd most voted PUP ticket). It's been present on our issue trackers since 2012, prompting heated implementation-related discussions on Redmine, JIRA, and Google Groups, with everything culminating in various abandoned pull requests on Puppet and adjacent modules.

Among the most important problems that could arise with the implementation of such a feature — and a very good reason for why this was overlooked for so long — is maintaining consistency. We provide support for a plethora of package providers in core Puppet, ranging from system to programming language package managers.

Need Help Getting to the Next Level with Puppet?

The Puppet Professional Services team can help you get more from your existing Puppet instance.


Each of these providers has their own understanding on package versioning and behaves differently when installing packages. Regardless of these differences, we have, as always, strived to offer a unified experience for our users by making version ranges convenient to use in spite of the user's personal experience with certain providers.

Added Version Range Support

In the Puppet 6.15.0 release, we added version range support to the apt, yum, gem and pip providers, along with a generic VersionRange class to facilitate improvement and implementation for additional providers.

For this initial iteration, we planned support for the most requested version range types, namely inequalities and intersections. Inequalities are represented by strings such as >1.0.1b or <2.3 and are used to ensure that the version of a package is greater or less than a specified version. Intersections are used to confine the version of a package between certain specified lower and upper bounds (e.g. >=1.0.1b <2.3).

Example: Mitigating a Docker CVE

As always, the best way to explain the usage of a feature is to provide a practical example. We chose Docker for this, as they provide repositories with multiple available versions of the package, and CVE-20190-5736 as the issue we're going to mitigate. For this issue, Docker recommends updating docker-ce to 18.09.2 or 18.06.3.

Prior to version ranges, you could've mitigated this CVE by setting the ensure parameter to either '18.09.2' or latest, but both of these options have their own drawbacks. Specifying a fixed version might not work right away and would stop the package from receiving any future updates (without manually incrementing the version). Ensuring latest works even if the correct version is not yet present on the repositories, but there is a high risk of overshooting — mistakenly bumping to the next major version and possibly introducing unwanted breaking changes.

To make use of version ranges, the following manifest can be applied:

package { 'docker-ce':
  ensure => '>=18.09.2',

This will make Puppet install the latest available version that respects the range constraint. If the installation is successful, future runs will report that the package is in the desired state.

If you are concerned about backward-incompatible changes and you don't necessarily want to alter the major version of Docker, you can make use of range intersections:

package { 'docker-ce':
  ensure => '>=18.09.2 <19',

This ensures that Puppet will not change the major version but will still upgrade from the affected version due to the lower bound of the range.

Further Development

As always, we welcome feedback and contributions on how to improve this new functionality. Don't hesitate to reach out on Slack if you have any questions!

Luchian Nemes is a software engineer at Puppet.

This post was originally published on June 16, 2020 and has been updated.

Visit the Puppet Forge