Creating RPM Packages for RedHat and Fedora

By DistroPack Team 7 min read

Creating RPM Packages for RedHat and Fedora

In the world of Linux distribution management, the ability to create reliable, well-structured RPM packages is an essential skill for system administrators, developers, and DevOps engineers. Whether you're packaging custom applications for RedHat Enterprise Linux, deploying internal tools on CentOS, or contributing to the Fedora ecosystem, understanding the RPM packaging system unlocks new levels of control and efficiency in software distribution.

RPM (Red Hat Package Manager) has been the cornerstone of package management in RedHat-based systems for decades, providing a robust framework for software installation, update, and dependency resolution. With the evolution from YUM to DNF as the default package manager in modern Fedora and RHEL systems, the underlying RPM format remains as relevant as ever.

Try DistroPack Free

Understanding RPM Package Structure

Before diving into the package building process, it's crucial to understand what makes up an RPM package. Contrary to what some might assume, an RPM is more than just a compressed archive—it's a sophisticated container with multiple components working together.

Core Components of RPM Packages

Every RPM package contains three essential elements:

Header: This section contains all the metadata about your package, including its name, version, release number, architecture, dependencies, changelog, and description. This information is what package managers like YUM and DNF use to make intelligent decisions about installation, updates, and dependency resolution.

Payload: The actual files that make up your software, compressed using cpio format and typically compressed with gzip or xz. This includes binaries, configuration files, documentation, and any other resources your application needs.

Scripts: RPM supports various scripts that execute at different points during the package lifecycle. These include pre-installation, post-installation, pre-removal, and post-removal scripts, allowing you to perform custom actions during package transactions.

The RPM Build Process: A Step-by-Step Guide

Building RPM packages involves a structured process that ensures consistency and reliability. Whether you're working with RedHat, Fedora, or CentOS, the fundamental steps remain largely the same.

1. Setting Up Your Build Environment

Before you begin, you'll need to set up a proper build environment. On Fedora, CentOS, or RHEL systems, you can install the necessary tools with:

sudo dnf install rpm-build rpmdevtools mock

The rpmdevtools package provides utilities that simplify the packaging process, while mock allows you to build packages in clean chroot environments, ensuring your builds aren't contaminated by your development system's specific configuration.

2. Creating the SPEC File

The heart of RPM packaging is the .spec file—a recipe that defines how to build your package. You can generate a template spec file using:

rpmdev-newspec your_application_name

A basic spec file includes several key sections:

Name:           your-application
Version:        1.0.0
Release:        1%{?dist}
Summary:        A brief description of your application

License:        MIT
URL:            https://example.com
Source0:        https://example.com/%{name}-%{version}.tar.gz

BuildRequires:  gcc, make
Requires:       bash, libcurl

%description
A longer description of your application and its functionality.

%prep
%autosetup

%build
make %{?_smp_mflags}

%install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}

%files
%license LICENSE
%doc README.md
/usr/bin/your-application

%changelog
* Tue Jan 01 2023 Your Name  - 1.0.0-1
- Initial package build

3. Building the RPM Package

With your spec file ready, you can build the RPM using rpmbuild:

rpmbuild -ba your-application.spec

This command will generate both binary and source RPM packages in the ~/rpmbuild/RPMS and ~/rpmbuild/SRPM directories respectively.

4. Testing and Validation

Before distributing your package, thoroughly test it using mock to ensure it builds correctly in a clean environment:

mock -r epel-8-x86_64 ~/rpmbuild/SRPMS/your-application-1.0.0-1.src.rpm

View Pricing

Advanced RPM Packaging Techniques

Once you've mastered the basics, several advanced techniques can enhance your RPM packages.

Managing Dependencies Effectively

Proper dependency management is crucial for RPM packages. Use these tags in your spec file:

Requires:       libexample >= 1.2.3
BuildRequires:  gcc, make, example-devel
Conflicts:      old-application-name
Obsoletes:      deprecated-application
Provides:       alternative-application-name

Understanding the distinction between runtime dependencies (Requires) and build dependencies (BuildRequires) is essential for creating efficient packages that install correctly on target systems.

Using RPM Macros

RPM provides numerous macros that simplify spec files and make them more portable across different RedHat-based distributions:

%{_bindir}      # Typically /usr/bin
%{_libdir}      # Typically /usr/lib64 or /usr/lib
%{_datadir}     # Typically /usr/share
%{_sysconfdir}  # Typically /etc

Handling Configuration Files

Configuration files require special handling in RPM packages. Mark them with the %config directive in the %files section:

%files
%config(noreplace) %{_sysconfdir}/your-application/config.conf

The noreplace option ensures that modified configuration files aren't overwritten during package updates.

Alternative Approach: Using FPM for Rapid Packaging

While traditional spec file-based packaging offers maximum control, sometimes you need a quicker solution. FPM (Effing Package Management) provides a streamlined approach to creating RPM packages without writing complex spec files.

fpm -s dir -t rpm -n mypackage -v 1.0.0 \
  -d "glibc >= 2.31" \
  --pre-install preinst.sh \
  --post-install postinst.sh \
  -C package-root .

This approach is particularly useful for simple applications or when you need to quickly package existing software without modifying its build system.

Package Versioning Best Practices

Consistent versioning is critical for effective package management. Follow these guidelines:

  • Use semantic versioning (MAJOR.MINOR.PATCH) for your software
  • Increment the release number (the part after the hyphen) when making packaging changes without changing the underlying software
  • Include distribution tags (e.g., .el8 for RHEL 8, .fc35 for Fedora 35) when building for specific distributions

Example: 1.2.3-1.el8 for the first build of version 1.2.3 for RHEL 8

Creating and Managing Repositories

Once you've built your RPM packages, you'll typically distribute them through repositories that YUM and DNF can access. The basic process involves:

# Create repository directory structure
mkdir -p /var/www/html/repo/{SRPMS,x86_64}

# Copy RPMs to appropriate directories
cp ~/rpmbuild/RPMS/x86_64/*.rpm /var/www/html/repo/x86_64/
cp ~/rpmbuild/SRPMS/*.rpm /var/www/html/repo/SRPMS/

# Generate repository metadata
createrepo_c /var/www/html/repo/x86_64/
createrepo_c /var/www/html/repo/SRPMS/

# Sign repository metadata (optional but recommended)
gpg --detach-sign --armor /var/www/html/repo/x86_64/repodata/repomd.xml

Best Practices for RPM Packaging

Follow these best practices to create high-quality RPM packages:

  • Always build in clean environments using mock to avoid implicit dependencies on your build system
  • Test packages thoroughly on fresh installations of target distributions
  • Sign your packages with GPG to ensure authenticity and integrity
  • Follow packaging guidelines specific to your target distribution (Fedora, RHEL, or CentOS)
  • Include comprehensive documentation in your packages
  • Use conditional builds for different distributions and architectures when necessary

Common Challenges and Solutions

Even experienced packagers encounter challenges. Here are solutions to common issues:

Dependency hell: Use automated dependency generators like rpmdep or manually verify dependencies with rpm -qpR package.rpm

Debugging failed builds: Examine build logs in ~/rpmbuild/BUILD/ or use mock --shell to enter the build environment and debug interactively

Multilib conflicts: Carefully manage architecture-specific files and use the %{_lib} macro family for portable path definitions

Configuration file management: Use %config(noreplace) for configuration files that users might modify

Conclusion

Mastering RPM package building for RedHat, Fedora, and CentOS systems is a valuable skill that enhances your ability to deploy and manage software efficiently. By understanding the structure of RPM packages, following the step-by-step build process, and implementing best practices for dependency management and versioning, you can create robust, reliable packages that integrate seamlessly with YUM and DNF package managers.

Remember that packaging is both an art and a science—while the technical aspects are crucial, understanding the user experience and maintaining consistency across updates is equally important. As you continue your packaging journey, consult distribution-specific guidelines and engage with the packaging communities around Fedora, RedHat, and CentOS to stay current with best practices.

Whether you're maintaining a few internal packages or contributing to the broader open source ecosystem, the skills you develop in RPM packaging will serve you well across the RedHat family of distributions and beyond.

Try DistroPack Free