Building and Distributing an RPM Package via Nexus YUM Repository: A Practical Deep Dive

Introduction

After successfully implementing a Debian-based distribution workflow, the next logical step is to replicate the same approach using the RPM ecosystem. This article walks through the complete lifecycle of building an RPM package, publishing it to a Nexus YUM repository, and consuming it from a clean client environment.

The focus is not only on how to execute each step, but more importantly on why each step exists, and how all components interact in a real-world DevOps environment.


1. Starting from a Clean Environment

docker run -it --rm --platform linux/amd64 -v $(pwd):/work rockylinux:9 bash

Why this matters

  • Ensures a controlled, reproducible environment
  • Forces amd64 architecture, matching the compiled binary
  • Avoids host contamination
  • Simulates real-world deployment targets

Packaging should always be validated in isolation.


2. Installing Required Tooling

dnf install -y rpm-build rpmdevtools

Why these tools

  • rpm-build: core toolchain to create RPM packages
  • rpmdevtools: utilities to bootstrap the RPM workspace

3. Creating the RPM Build Structure

rpmdev-setuptree

This generates:

~/rpmbuild/
├── BUILD
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS

Why this structure exists

RPM enforces a strict separation:

  • SOURCES → input artifacts (your binary)
  • SPECS → build instructions
  • RPMS → final output

This design ensures deterministic and repeatable builds.


4. Preparing the Binary

cp /work/git-semver ~/rpmbuild/SOURCES/

Why this step

The RPM build process only works with files inside its controlled environment (SOURCES). This guarantees:

  • Reproducibility
  • Isolation from external filesystem changes

5. Defining the SPEC File

vi ~/rpmbuild/SPECS/git-semver.spec

Minimal Working SPEC

Name:           git-semver
Version:        0.2.1
Release:        1
Summary:        Git semantic version tool

License:        MIT
BuildArch:      x86_64
Requires:       git

%description
Simple semantic versioning tool.

%install
mkdir -p %{buildroot}/usr/local/bin
cp %{_sourcedir}/git-semver %{buildroot}/usr/local/bin/git-semver
chmod +x %{buildroot}/usr/local/bin/git-semver

%files
/usr/local/bin/git-semver

6. Understanding the SPEC File

Key Elements

  • Name, Version, Release: uniquely identify the package
  • BuildArch: ensures architecture compatibility
  • Requires: declares runtime dependencies

Important Note

Requires: git

This ensures:

  • Automatic dependency resolution
  • No runtime failures due to missing Git

Dependency management must be handled by the package manager, not by the application.


7. Building the RPM Package

rpmbuild -ba ~/rpmbuild/SPECS/git-semver.spec

Output

~/rpmbuild/RPMS/x86_64/git-semver-0.2.1-1.x86_64.rpm

Why this step is critical

  • Validates SPEC correctness
  • Produces a distributable artifact
  • Ensures filesystem layout is respected

8. Local Installation Test

dnf install -y ~/rpmbuild/RPMS/x86_64/git-semver-0.2.1-1.x86_64.rpm
git-semver --version
git-semver version v0.2.1

Why test locally first

  • Isolates packaging issues
  • Confirms binary works after installation
  • Avoids debugging repository problems prematurely

9. Creating the Nexus YUM Repository

In Nexus Repository Manager, create a YUM (hosted) repository with:

  • Repodata Depth: 0
  • Deployment Policy: Allow redeploy

Why these settings matter

Repodata Depth = 0

  • Assumes a flat repository structure
  • Nexus scans only the root for RPMs
  • Simplifies metadata generation

Deployment Policy

  • Allows iterative development
  • Enables overwriting packages during testing

10. Uploading the RPM

curl -k -u usr_jenkins_nexus:1234qwer \
  --upload-file /root/rpmbuild/RPMS/x86_64/git-semver-0.2.1-1.x86_64.rpm \
  https://nexus.devops-db.internal/repository/devops-db-yum/

Why this works

  • Nexus automatically detects RPM uploads
  • Extracts metadata
  • Triggers repository indexing

11. The Critical Step: Metadata (repodata)

This is the most important concept in YUM repositories.

YUM does not scan files directly.

Instead, it relies entirely on:

If repodata does not exist:

dnf install git-semver

→ will always fail


12. Rebuilding Repository Metadata

Normally automatic, but if needed:

  • Go to Nexus
  • Repository → “Rebuild index”

Why this matters

This step:

  • Scans RPMs
  • Generates metadata
  • Makes packages visible to clients

Without metadata, the repository is effectively empty.


13. Preparing the Client Environment

Clean container

docker run -it --rm --platform linux/amd64 -v $(pwd):/work rockylinux:9 bash

Install internal CA

curl -k -o roots.pem https://ca.devops-db.internal/roots.pem

mkdir -p /etc/pki/ca-trust/source/anchors/
cp roots.pem /etc/pki/ca-trust/source/anchors/devops-db-root.crt

update-ca-trust

Why this is required

  • Nexus uses an internal certificate
  • System must trust the issuing CA
  • Otherwise, TLS validation fails

14. Configuring the YUM Repository

cat <<EOF > /etc/yum.repos.d/git-semver.repo
[git-semver]
name=Git Semver Repo
baseurl=https://nexus.devops-db.internal/repository/devops-db-yum/
enabled=1
gpgcheck=0
repo_gpgcheck=0
EOF

15. Refreshing Metadata

dnf clean all
dnf makecache

16. Verifying Availability

dnf repoquery git-semver
Last metadata expiration check: 0:00:08 ago on Thu Mar 26 18:44:58 2026.
git-semver-0:0.2.1-1.x86_64

17. Installing from Repository

dnf install -y git-semver

18. Final Validation

[root@4dcbd74240ed /]# git-semver --version
git-semver version v0.2.1

Leave a Reply

Your email address will not be published. Required fields are marked *