+ Last review: +

Naming and identifying modules in Fedora

With the introduction of Modularity to the packager ecosystem it also introduced several challenges. First was that the NEVRA (name-epoch-version-release-architecture) package naming convention used in Fedora was insufficient. Modules are uniquely identified by the NSVCA naming convention, which stands for name, stream, version, context, and architecture. This page describes both the source-level ID (NSV) and the binary-level ID (NSVCA).

Source-level module ID (NSV)

At the source level, modules are only identified by the first three: name, stream, and version. Name is defined as a name of the module’s repository in DistGit, stream is a name of the branch in DistGit, and version is the timestamp of a commit.


Name of the module corresponds to the name of the application or the language stack it represents. An example of a name could be postgresql for a PostgreSQL database module, or nodejs for a Node.js runtime.


Streams are variants of a module with a certain promise.

In most cases, streams promise backwards compatibility with a major version of the application or the language stack they provide. For example, let’s say the Node.js runtime is supported in two major versions: 6 and 8. In this case, the module nodejs would have two streams: 6 and 8.

However, streams can also promise different things such as stability. A good example of this is the calc package in Fedora which is maintained in two upstream branches: stable for the latest stable release and unstable for the latest development version. Using modularity, this package could be built as a calc module in two different streams: stable and unstable.

In addition to the version promise, streams are also a way for packagers to communicate the level of maintenance. Does the maintainer plan to apply every minor patch? Will they apply security fixes quickly? Or is the module updated only twice a year? This can also be part of the promise.

Another different example could be a stream that provides the software compiled using some experimental flags increasing the performance.

Anyway, you get the idea. Streams are a very flexible and powerful tool. Use them wisely.


Versions are just updates of a given stream. Technically, version is a number generated by the build system. Higher number always wins. This means that version does not identify a major/minor version of the software but the update/commit to a particular stream.

Binary-level ID (NSVCA)

Building a module from one source can result in multiple different binaries. Different binaries are typically produced for different architectures (i.e. x86_64, armv7hl, etc.) and different Fedora releases (i.e. Fedora 28, Fedora 29, EPEL 7, etc.).

Module binaries use the full NSVCA — so in addition to the name, stream, and version fields described above, there are two more for binaries: architecture and context.


Fedora is built for many different architectures. The architecture field simply distinguishes architecture-specific binaries from each other. The value is typically the same as with RPM packages, i.e. x86_64, armv7hl, etc.


Context is used to distinguish binaries built for different Fedora releases. Thanks to stream expansion, modules can also be built against multiple streams of other modules, i.e. different versions of a language runtime etc.

The value is generated by the build system and is usually hidden from the user as it doesn’t have any informational value by itself — it is a hash. However, the client tooling consuming this value can present it in a useful way.

One way of representing the context could be listing the Fedora releases for which a certain module has been built.

NSVCA Definition

This defines naming policy for modulemd metadata of final (built) modules. This policy does NOT apply on sources such as modulemd yaml in dist-git.

The goal is to provide unique identifiers for modules that are both human readable and also suitable for machine processing.


  • N - Name

  • S - Stream

  • V - Version

  • C - Context

  • A - Arch

  • P - Profile


Fields are separated with ':' (colon): N:S:V:C:A.

If P is specified, it’s separated from N:S:V:C:A with '/' (forward slash): N:S:V:C:A/P.


# N:S:V:C:A
# N:S:V:C:A/P


A form is a sequence of fields that fully or partially identifies a module.

Full Forms


Unique identifier of a module.


Unique identifier of a module profile.

Partial Forms

Supported partial forms are: N [ : S [ :V [ :C ] ] ] [ :A ] [ /P ]


  • N

  • N::A

  • N:S

  • N:S::A

  • N:S:V

  • N:S:V::A

  • N:S:V:C

  • N:S:V:C:A (identical to N:S:V:C::A)

  • and all combinations with /P

Missing fields SHOULD be populated with recommended defaults:


defaults to the enabled or system default stream for the module in this particular order


defaults to the latest available version in the module stream


defaults to a value matching with already installed modules or modules involved in the transaction (not yet installed)


defaults to the system arch (e.g. DNF’s $basearch)


defaults to the system default or 'default' profile

Allowed Characters

N - Name

a-z A-Z 0-9 . - _

S - Stream

a-z A-Z 0-9 . - _

V - Version


C - Context

0-9 a-f

A - Arch

a-z A-Z 0-9 . - _

P - Profile

a-z A-Z 0-9 . - _

All fields MUST start and end with an alphanumeric character: a-z A-Z 0-9

Forbidden Characters

This paragraph serves as a design decision for future changes.

Following characters MUST NOT be part of any field:

  • : (colon) - separator

  • / (forward slash) - profile separator

  • \ (backslash) - comon control character

  • * (asterisk) - common wildcard

  • ? (question mark) - common wildcard

  • @ (at) - grpspec in YUM and DNF

  • ` ` (space) - common separator