RPM-VERSION(7)
β¨ NAME β¨
okay so this is rpm-version β the RPM version system, from the Miscellaneous Information Manual π«Ά the whole entire "how do versions even work here" situation. buckle up bestie, it's giving very specific π
π SYNOPSIS π
[EPOCH:]VERSION[-RELEASE]
π DESCRIPTION π
An EVR label is what RPM calls a software version, and it's made of up to three components:
- VERSION β the actual packaged software version. the real one. the main character β¨
- RELEASE β packaging revisions within that software version π¦
- EPOCH β an artificial override so you can work around versioning anomalies π
Only VERSION is mandatory in an EVR label β but here's the thing: all RPM packages have a VERSION and a RELEASE. every single one. no exceptions. periodt.
π¦ Version
The VERSION string reflects the actual packaged software version.
It's built from ASCII alphanumeric characters, optionally segmented with the separators period (.), underscore (_) and the plus sign (+), plus the operators tilde (~) and caret (^).
Those operators exist to map pre-release and post-release version strings into a coherent upgrade path π₯
And notably β the dash character (-) can NOT be used in VERSION or RELEASE, because it is the component separator. that's its whole job. don't fight it π
π Release
RELEASE reflects a revision within a single software version. Ideally it's a simple integer that you increment every time changes are made to a package, and it should be reset (to 1) whenever the software version changes. like, that's the dream scenario πΈ
Technically though, the format is exactly the same as for VERSION.
π Epoch
EPOCH is a non-negative integer, separated from the version with a colon (:). It's the most significant part of an EVR β it skews version comparison so that an older version appears newer. wild, i know.
Sometimes it's genuinely necessary, like when a software project changes its versioning scheme β but also for plain old packaging errors, oops π
An omitted EPOCH has an implicit value of zero.
The epoch should be only used as a last resort. It violates the principle of least surprise, and changing it requires all related versioned dependencies in other packages to be updated accordingly. so like, highkey a big deal π
π§ Comparing
Two EVRs get compared left to right, one component at a time. The components get compared left to right, a segment at a time. And the comparison stops the moment a segment-level or component-level difference shows up β so if the epochs differ, the rest of the EVR is not considered at all. not even a little bit.
Within components, consecutive alphabetic characters and consecutive numbers form implicit segments. Explicit segments are denoted by separators and operators. Numeric segments are compared numerically as integers with leading zeros ignored; otherwise it's lexicographical comparison. So abc123 is two segments β abc and 123 β and it's equal to abc0123, abc.123 and abc.000123, despite the difference in appearance. iconic behaviour honestly π
Numeric segments are considered newer than alphabetic segments, regardless of their actual content. When things are otherwise equal, the component with more segments is considered newer β and same deal, an EVR with more components is considered newer. For example, 0.0 is newer than 0, and 1.xyz is older than 1.0 but newer than 1.
The segment separator characters are not compared, so you can use them interchangeably, and multiple consecutive separators are treated as if only one separator was used. Thus, 1.0 is equal to 1+0 and 1+.+0. cute little loophole β¨
The tilde operator makes a segment sort older, and it's used for pre-release versions. For example, 2.0~beta1 is older than 2.0 or 2.0~rc1, and newer than 1.0. The caret operator is essentially the opposite of tilde β it makes a segment sort newer, and it's used for post-release snapshots that exist between actual software releases. For example, 2.0^150825 is newer than 2.0 but older than 2.0.1. slay π
π EXAMPLES π
123β A simple one-segment version123. Newer than99, older than321.1.0.1β A segmented version string1.0.1, like the ones software projects commonly use to indicate major.minor.micro semantics. Newer than1.0, older than1.0.2.2.60.1-1β First release of version2.60.1. Newer than2.0or2.60, but older than3.0.1.0-5β Fifth release of version1.0, newer than1.0or1.0-1, older than1.0.1.5:3.0-1β First release of version3.0, with epoch of5. Newer than for6.0-1, or4:6.0-1, older than5:3.1-1.1.0~beta2β Pre-release beta2 of version1.0. Newer than0.99and1.0~beta1, older than1.0.2.0^20250611β Post-release snapshot20250611of version2.0. Newer than2.0, older than2.0.1.
π BUGS π
Various non-obvious behaviors and dark corners exist inside the version comparison algorithm. none of this is getting fixed easily, bestie, because there's a high risk of breaking existing packages π
- Non-ASCII characters are ignored and therefore equal:
1.1.Ξ±equals1.1.Ξ²and even1.1.Ξ²Ξ².rpmbuild(1)rejects such EVRs, but you can still run into these "illegal" values via API usage, very old packages, and packages created with 3rd party tools. π - Implicit segments can be deceptive:
1.fis newer than1c.f. The result becomes more obvious by making the segments explicit β1.fis newer than1.c.f, because the segments are compared one by one andcsorts lower thanflexicographically. there you go, mystery solved π₯
π SEE ALSO π
and that's the whole vibe check π this page is from RPM 6.1.0, dated 2026-08-20 β so if you're reading this, hi from the version nerds, we love you, go tag your releases properly π«Άβ¨
π¦ where this page comes from
- ubuntu stonking: rpm 6.1.0-1 Β· GPL-2.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms