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 usednf/yumon RHEL-family systems; build steps were verified with theglc-demopackage 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:
sudo dnf install -y rpm-build rpmdevtools gcc makeOn releases that still use yum:
sudo yum install -y rpm-build rpmdevtools gcc makerpmdevtools 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:
rpmdev-setuptreeThat creates five empty directories:
~/rpmbuild/
├── BUILD
├── RPMS
├── SOURCES
├── SPECS
└── SRPMSIf rpmdev-setuptree is not installed:
mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}Confirm the layout:
tree ~/rpmbuildExample:
/home/user/rpmbuild
├── BUILD
├── RPMS
├── SOURCES
├── SPECS
└── SRPMSMethod 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
mkdir -p ~/work/glc-src-rpm
cd ~/work/glc-src-rpm
rpm2cpio ~/rpmbuild/SRPMS/glc-demo-1.0-1.src.rpm | cpio -idm
ls -laYou should see the SPEC file and the original tarball:
glc-demo-1.0-1.tar.gz
glc-demo.specStep 2 — Unpack the tarball and change the source
mkdir work
cd work
tar -xzf ../glc-demo-1.0-1.tar.gzShow the tree:
find glc-demo-1.0 -type fEdit a file—here we append a line to the README to simulate a real patch:
echo 'PATCH_MARKER from source RPM rebuild' >> glc-demo-1.0/usr/share/doc/glc-demo/READMEStep 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:
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 -5Copy the new tarball and SPEC into the rpmbuild tree:
cp glc-demo-1.0-2.tar.gz ~/rpmbuild/SOURCES/
cp glc-demo.spec ~/rpmbuild/SPECS/glc-demo.specStep 4 — Update the SPEC file for release 2
Edit ~/rpmbuild/SPECS/glc-demo.spec:
- change
Release:from1to2 - add a
%changelogentry 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:
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.rpmStep 5 — Build again
rpmbuild -ba ~/rpmbuild/SPECS/glc-demo.specExample output:
Wrote: /home/user/rpmbuild/SRPMS/glc-demo-1.0-2.src.rpm
Wrote: /home/user/rpmbuild/RPMS/noarch/glc-demo-1.0-2.noarch.rpmIf you only need to rebuild the same source RPM unchanged (no edits to SPEC or tarball), use:
rpmbuild --rebuild ~/rpmbuild/SRPMS/glc-demo-1.0-1.src.rpmThat 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:
/usr/bin/glc-demo
/etc/glc-demo/config
/usr/share/doc/glc-demo/READMEStep 1 — Create the source directory and files
Work under /tmp so you do not touch system directories:
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.
EOFList what you created:
find "$STAGING" -type f | sortExample:
/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/READMEStep 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):
tar -C /tmp -czf ~/rpmbuild/SOURCES/glc-demo-1.0-1.tar.gz glc-demo-1.0Verify the paths inside the archive (note the top-level glc-demo-1.0/ directory):
tar -tzf ~/rpmbuild/SOURCES/glc-demo-1.0-1.tar.gzExample (trimmed):
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/READMEls -lh ~/rpmbuild/SOURCES/Step 3 — Write the SPEC file
Create ~/rpmbuild/SPECS/glc-demo.spec:
vi ~/rpmbuild/SPECS/glc-demo.specPaste:
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 releaseThe %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
rpmbuild -ba ~/rpmbuild/SPECS/glc-demo.specOn success you should see lines like:
Wrote: /home/user/rpmbuild/SRPMS/glc-demo-1.0-1.src.rpm
Wrote: /home/user/rpmbuild/RPMS/noarch/glc-demo-1.0-1.noarch.rpmList the results:
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:
rpm -qlp ~/rpmbuild/RPMS/noarch/glc-demo-1.0-2.noarch.rpmExample:
/etc/glc-demo/config
/usr/bin/glc-demo
/usr/share/doc/glc-demo/READMECheck digests:
rpm -K ~/rpmbuild/RPMS/noarch/glc-demo-1.0-2.noarch.rpmExample:
/home/user/rpmbuild/RPMS/noarch/glc-demo-1.0-2.noarch.rpm: digests OKOn a RHEL-family host, install and run the script:
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-demoRemove the test package when finished:
sudo rpm -e glc-demoCommon 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.

