Build RPM Package with rpmbuild

Learn how to build an RPM package in Linux using rpmbuild. Create the rpmbuild directory structure, write a SPEC file, build RPM from source, create RPM from scratch, and verify the package.

Published

Updated

Read time 7 min read

Reviewed byDeepak Prasad

Build RPM Package with rpmbuild

You build RPM packages with rpmbuild, a SPEC file, and the standard ~/rpmbuild directory tree. Method 1 rebuilds an existing source RPM (.src.rpm). Method 2 creates a new package from scratch. Both use the same demo package (glc-demo) with real directories and files. For the hands-on lab, do Method 2 first to produce glc-demo-1.0-1.src.rpm, then return to Method 1 to modify and rebuild it—or use any .src.rpm from your distribution in Method 1 instead.

Tested on: RPM version 4.20.1 (rpmbuild); kernel 6.14.0-37-generic; Ubuntu 25.04. Install commands use dnf/yum on RHEL-family systems; build steps were verified with the glc-demo package below.


What is rpmbuild?

rpmbuild reads a SPEC file—a recipe with the package name, version, source archive, build commands, and file list—and writes:

  • a binary RPM under ~/rpmbuild/RPMS/
  • a source RPM (.src.rpm) under ~/rpmbuild/SRPMS/ when you pass -ba

Sources live in ~/rpmbuild/SOURCES/, the SPEC in ~/rpmbuild/SPECS/, and temporary build files under BUILD/ and BUILDROOT/.


Install required RPM build tools

On RHEL, AlmaLinux, Rocky Linux, or Fedora:

bash
sudo dnf install -y rpm-build rpmdevtools gcc make

On releases that still use yum:

bash
sudo yum install -y rpm-build rpmdevtools gcc make

rpmdevtools includes rpmdev-setuptree. Heavier packages (for example httpd) also need -devel RPMs listed under BuildRequires in the SPEC file.

If you must collect RPMs without a full online repo, see download an RPM and all dependencies.


Create the rpmbuild directory structure

From your home directory:

bash
rpmdev-setuptree

That creates five empty directories:

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

If rpmdev-setuptree is not installed:

bash
mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}

Confirm the layout:

bash
tree ~/rpmbuild

Example:

text
/home/user/rpmbuild
├── BUILD
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS

Method 1: Build RPM from source RPM

Use this when you start from an existing source RPM (.src.rpm)—from your distribution (for example nginx or httpd source RPMs) or from Method 2 below (glc-demo-1.0-1.src.rpm).

For the glc-demo lab, complete Method 2 first so ~/rpmbuild/SRPMS/glc-demo-1.0-1.src.rpm exists, then run the steps here.

Step 1 — Extract the source RPM into a working directory

bash
mkdir -p ~/work/glc-src-rpm
cd ~/work/glc-src-rpm
rpm2cpio ~/rpmbuild/SRPMS/glc-demo-1.0-1.src.rpm | cpio -idm
ls -la

You should see the SPEC file and the original tarball:

text
glc-demo-1.0-1.tar.gz
glc-demo.spec

Step 2 — Unpack the tarball and change the source

bash
mkdir work
cd work
tar -xzf ../glc-demo-1.0-1.tar.gz

Show the tree:

bash
find glc-demo-1.0 -type f

Edit a file—here we append a line to the README to simulate a real patch:

bash
echo 'PATCH_MARKER from source RPM rebuild' >> glc-demo-1.0/usr/share/doc/glc-demo/README

Step 3 — Repack the tarball with a new release number

Bump the release from 1 to 2, so the archive name becomes glc-demo-1.0-2.tar.gz:

bash
cd ~/work/glc-src-rpm/work
tar -czf ../glc-demo-1.0-2.tar.gz glc-demo-1.0
cd ~/work/glc-src-rpm
tar -tzf glc-demo-1.0-2.tar.gz | head -5

Copy the new tarball and SPEC into the rpmbuild tree:

bash
cp glc-demo-1.0-2.tar.gz ~/rpmbuild/SOURCES/
cp glc-demo.spec ~/rpmbuild/SPECS/glc-demo.spec

Step 4 — Update the SPEC file for release 2

Edit ~/rpmbuild/SPECS/glc-demo.spec:

  • change Release: from 1 to 2
  • add a %changelog entry describing your change

The Source0 line can stay as %{name}-%{version}-%{release}.tar.gz—with Release: 2 that resolves to glc-demo-1.0-2.tar.gz.

Example header and changelog tail:

spec
Release:        2
...
%changelog
* Sun Jun 22 2025 GoLinuxCloud <admin@golinuxcloud.com> - 1.0-1
- Initial release
* Mon Jun 23 2025 GoLinuxCloud <admin@golinuxcloud.com> - 1.0-2
- Updated README after modifying source from src.rpm

Step 5 — Build again

bash
rpmbuild -ba ~/rpmbuild/SPECS/glc-demo.spec

Example output:

text
Wrote: /home/user/rpmbuild/SRPMS/glc-demo-1.0-2.src.rpm
Wrote: /home/user/rpmbuild/RPMS/noarch/glc-demo-1.0-2.noarch.rpm

If you only need to rebuild the same source RPM unchanged (no edits to SPEC or tarball), use:

bash
rpmbuild --rebuild ~/rpmbuild/SRPMS/glc-demo-1.0-1.src.rpm

That skips extract/edit steps and is common for kernel or httpd source RPMs when you only recompile for your machine.

When rpmbuild reports Failed build dependencies, install the listed packages, then rerun the command. See how to get a complete dependency list for an RPM.


Method 2: Create RPM package from scratch

Use this when no .src.rpm exists yet. You create the files that should appear on the installed system, pack them into a tarball, write a SPEC file, and run rpmbuild -ba.

The demo package glc-demo installs three paths:

text
/usr/bin/glc-demo
/etc/glc-demo/config
/usr/share/doc/glc-demo/README

Step 1 — Create the source directory and files

Work under /tmp so you do not touch system directories:

bash
PKG=glc-demo
VER=1.0
STAGING=/tmp/${PKG}-${VER}

rm -rf "$STAGING"
mkdir -p "$STAGING/usr/bin"
mkdir -p "$STAGING/etc/${PKG}"
mkdir -p "$STAGING/usr/share/doc/${PKG}"

cat > "$STAGING/usr/bin/${PKG}" << 'EOF'
#!/bin/sh
echo 'GLC demo tools v1.0'
EOF
chmod 755 "$STAGING/usr/bin/${PKG}"

cat > "$STAGING/etc/${PKG}/config" << 'EOF'
# glc-demo configuration
ENABLED=1
EOF

cat > "$STAGING/usr/share/doc/${PKG}/README" << 'EOF'
GLC demo RPM package for rpmbuild article.
EOF

List what you created:

bash
find "$STAGING" -type f | sort

Example:

text
/tmp/glc-demo-1.0/etc/glc-demo/config
/tmp/glc-demo-1.0/usr/bin/glc-demo
/tmp/glc-demo-1.0/usr/share/doc/glc-demo/README

Step 2 — Create the source tarball in SOURCES/

The archive name must match Source0 in the SPEC file (glc-demo-1.0-1.tar.gz for version 1.0 and release 1):

bash
tar -C /tmp -czf ~/rpmbuild/SOURCES/glc-demo-1.0-1.tar.gz glc-demo-1.0

Verify the paths inside the archive (note the top-level glc-demo-1.0/ directory):

bash
tar -tzf ~/rpmbuild/SOURCES/glc-demo-1.0-1.tar.gz

Example (trimmed):

text
glc-demo-1.0/
glc-demo-1.0/usr/bin/glc-demo
glc-demo-1.0/etc/glc-demo/config
glc-demo-1.0/usr/share/doc/glc-demo/README
bash
ls -lh ~/rpmbuild/SOURCES/

Step 3 — Write the SPEC file

Create ~/rpmbuild/SPECS/glc-demo.spec:

bash
vi ~/rpmbuild/SPECS/glc-demo.spec

Paste:

spec
Name:           glc-demo
Version:        1.0
Release:        1
Summary:        Demo tools package with script, config, and docs
License:        MIT
URL:            https://www.golinuxcloud.com
Source0:        %{name}-%{version}-%{release}.tar.gz
BuildArch:      noarch

%description
Demo package that installs a script, config file, and README.

%prep
%autosetup -n glc-demo-1.0

%build
# script-only package — nothing to compile

%install
rm -rf %{buildroot}
mkdir -p %{buildroot}/usr/bin
mkdir -p %{buildroot}/etc/glc-demo
mkdir -p %{buildroot}/usr/share/doc/glc-demo
install -m 755 usr/bin/glc-demo %{buildroot}/usr/bin/glc-demo
install -m 644 etc/glc-demo/config %{buildroot}/etc/glc-demo/config
install -m 644 usr/share/doc/glc-demo/README %{buildroot}/usr/share/doc/glc-demo/README

%files
/usr/bin/glc-demo
/etc/glc-demo/config
/usr/share/doc/glc-demo/README

%changelog
* Sun Jun 22 2025 GoLinuxCloud <admin@golinuxcloud.com> - 1.0-1
- Initial release

The %install section copies files into %{buildroot} exactly as they should appear on disk. The %files section must list every installed path.

Step 4 — Build the binary and source RPM

bash
rpmbuild -ba ~/rpmbuild/SPECS/glc-demo.spec

On success you should see lines like:

text
Wrote: /home/user/rpmbuild/SRPMS/glc-demo-1.0-1.src.rpm
Wrote: /home/user/rpmbuild/RPMS/noarch/glc-demo-1.0-1.noarch.rpm

List the results:

bash
ls -lh ~/rpmbuild/RPMS/noarch/
ls -lh ~/rpmbuild/SRPMS/

This produces glc-demo-1.0-1.src.rpm in ~/rpmbuild/SRPMS/—the file Method 1 extracts and rebuilds.


Verify and install the built RPM

Inspect package contents without installing:

bash
rpm -qlp ~/rpmbuild/RPMS/noarch/glc-demo-1.0-2.noarch.rpm

Example:

text
/etc/glc-demo/config
/usr/bin/glc-demo
/usr/share/doc/glc-demo/README

Check digests:

bash
rpm -K ~/rpmbuild/RPMS/noarch/glc-demo-1.0-2.noarch.rpm

Example:

text
/home/user/rpmbuild/RPMS/noarch/glc-demo-1.0-2.noarch.rpm: digests OK

On a RHEL-family host, install and run the script:

bash
sudo rpm -ivh ~/rpmbuild/RPMS/noarch/glc-demo-1.0-2.noarch.rpm
glc-demo
cat /usr/share/doc/glc-demo/README
rpm -qa | grep glc-demo

Remove the test package when finished:

bash
sudo rpm -e glc-demo

Common rpmbuild errors

Error What it means What to do
Failed build dependencies: Missing BuildRequires packages sudo dnf install … for each name, then rerun rpmbuild
File not found: … in %files %install did not create a listed path Fix install lines or the %files list
error: Bad source: … Tarball missing or wrong name in SOURCES/ Match filename to Source0 (including release number)
%autosetup / unpack failure Tarball lacks expected top directory Use tar -tzf and align %prep with the archive layout
Macro %dist has empty body Non-RHEL host without distro macros Set Release: 1 without %{?dist} or define %dist in ~/.rpmmacros

Read the last part of the rpmbuild log; the first error above the final exit 0 is usually the one to fix.


Summary

To build from a source RPM, extract it with rpm2cpio, unpack and edit the tarball, bump Release in the SPEC, copy the new tarball to SOURCES/, and run rpmbuild -ba again—or use rpmbuild --rebuild when nothing changed. To create an RPM from scratch, make a staging directory under /tmp, add the files and directories the installed package should contain, tarball that tree into ~/rpmbuild/SOURCES/, write a SPEC under ~/rpmbuild/SPECS/, and run rpmbuild -ba (that also writes the .src.rpm for Method 1). Verify with rpm -qlp and rpm -K, install with rpm -ivh on a test host, and install any missing build dependencies when errors list BuildRequires packages.


Frequently Asked Questions

1. What is the difference between building from a source RPM and from scratch?

A source RPM (.src.rpm) already contains a SPEC file and source archive—you rebuild or modify that recipe. From scratch you create the directories, files, tarball, and SPEC yourself, then run rpmbuild -ba.

2. Where does rpmbuild put finished RPM files?

Binary RPMs land under ~/rpmbuild/RPMS// and source RPMs under ~/rpmbuild/SRPMS/ when you use the standard topdir layout.

3. What does rpmbuild -ba do?

It runs the prep, build, and install sections from the SPEC file and produces both a binary RPM and a source RPM.

4. How do I fix Failed build dependencies from rpmbuild?

Install the packages listed after BuildRequires in the SPEC file (or shown in the rpmbuild error), then run rpmbuild again.

References

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …