Copr supports building RPM modules using modulemd format. Modules are collections of packages with defined streams, profiles, and dependencies.
Overview
Modules in Copr allow you to:
Build collections of related packages as a unit
Define multiple streams of the same module
Create installation profiles for different use cases
Specify module-level dependencies
Module Model
Modules are represented in the database with these key attributes:
class Module :
id : Integer # Unique identifier
name: String # Module name (e.g., 'nodejs')
stream: String # Stream version (e.g., '18', '20')
version: BigInteger # Module version/build number
summary: String # Short description
description: Text # Detailed description
yaml_b64: Text # Base64-encoded modulemd YAML
The yaml_b64 field stores the original modulemd YAML. This preserves the exact file structure and any custom fields not directly mapped to model attributes.
Module Status
Modules can be in one of these states:
Status Description waitingModule build is queued pendingModule build is pending startingModule build is starting runningModule build is in progress succeededModule built successfully failedModule build failed canceledModule build was canceled unknownStatus cannot be determined
The module status is derived from:
Associated backend action result (if exists)
Individual build statuses within the module
Creating Modules
Using the Web Interface
Navigate to your project
Go to the “Modules” tab
Click “Build Module”
Provide modulemd YAML content
Using the API
from copr.v3 import Client
client = Client.create_from_config_file()
modulemd = """
data:
name: my-module
stream: "1.0"
version: 1
summary: My example module
description: A module for demonstration
license:
module: [MIT]
components:
rpms:
my-package:
rationale: Main package
ref: main
"""
result = client.module_proxy.build_from_file(
ownername = "@mygroup" ,
projectname = "myproject" ,
path = "/path/to/modulemd.yaml"
)
Modulemd Structure
document : modulemd
version : 2
data :
name : nodejs
stream : "20"
version : 1
summary : JavaScript runtime
description : >
Node.js is a JavaScript runtime built on Chrome's V8
JavaScript engine.
license :
module :
- MIT
dependencies :
- buildrequires :
platform : [ el8 ]
requires :
platform : [ el8 ]
profiles :
default :
rpms :
- nodejs
- npm
development :
rpms :
- nodejs
- npm
- nodejs-devel
api :
rpms :
- nodejs
- npm
filter :
rpms :
- nodejs-private-libs
components :
rpms :
nodejs :
rationale : Primary runtime
ref : v20.0.0
buildorder : 1
Module Properties
Name-Stream-Version (NSV)
Modules are uniquely identified by their NSV:
module.nsv # Returns "nodejs-20-1"
module.full_name # Returns "@group/project/nodejs-20-1"
RPM Filters and APIs
Control which packages are visible:
module.rpm_filter # Packages excluded from installation
module.rpm_api # Packages part of the module API
Profiles
Define installation profiles for different use cases:
module.profiles
# Returns: {
# 'default': ['nodejs', 'npm'],
# 'development': ['nodejs', 'npm', 'nodejs-devel']
# }
Components
Access module component information:
module.components
# Returns dict of RPM components with their metadata
Module Builds and Relationships
Modules are associated with regular package builds:
module.builds # List of Build objects in this module
module.action # Backend action for module creation
Builds can be part of a module:
build.module_id # Foreign key to module
build.module # Related Module object
Module Hotfixes
Projects can enable module hotfixes, allowing packages to override module content:
copr.module_hotfixes = True # Enable module hotfixes
Enabling module_hotfixes affects how DNF resolves dependencies when modules are enabled. Use with caution.
Module Chroot Configuration
Configure module-specific settings per chroot:
copr_chroot.module_toggle # Module setup commands
Example module toggle commands:
# Enable a module stream
module enable nodejs:20
# Install a module profile
module install nodejs:20/default
# Disable a module
module disable nodejs
Working with Modules
Query Modules
Query by NSV
Query by Project
from coprs.models import Module
module = Module.query.filter_by(
name = "nodejs" ,
stream = "20" ,
version = 1
).first()
Check Module Status
if module.status == ModuleStatusEnum( "succeeded" ):
print ( f "Module { module.nsv } built successfully" )
else :
print ( f "Module status: { module.state } " )
Access Module URL
Modules are available at the project’s module URL:
modules_url = copr.modules_url
# Returns: "https://download.copr.../results/owner/project/modules"
Module Actions
Module creation triggers backend actions:
action = module.action
if action:
print ( f "Action type: { action.action_type } " )
print ( f "Result: { action.result } " )
print ( f "Message: { action.message } " )
Action states:
waiting: Action queued for backend
success: Module created successfully
failure: Module creation failed
Unique Constraints
Only one module with a specific name-stream-version combination can exist per project: UNIQUE (copr_id, name , stream, version )
Attempting to create a duplicate will fail.
API Serialization
When modules are serialized for API responses:
action_dict = action.to_dict()
if action_dict.get( "object_type" ) == "module" :
# Additional module-specific data is included:
# - projectname
# - ownername
# - modulemd_b64 (base64-encoded YAML)
Best Practices
Version incrementing : Increment module version for each rebuild
Stream stability : Keep streams consistent - don’t change package versions drastically within a stream
Dependencies : Clearly specify module dependencies in buildrequires and requires
Profiles : Define meaningful profiles for common use cases
Testing : Test module installations with different profiles
Documentation : Document module purpose and usage in description
Limitations
Modules must have unique name-stream-version per project
Module YAML must be valid modulemd format (version 2 recommended)
Component packages must exist in the project or dependencies
Module builds depend on successful component package builds
Further Reading