Building RPM Packages in RHEL


๐ Hello Guys,
Have you ever wondered how the commands we use in Linux are actually created?
If yes, then you're on the right page โ and Iโve got your back! ๐
In this post, Iโll walk you through the process of creating your own Linux command โ one that can even surpass existing ones or fulfill a completely new purpose you envision.
So, without further ado, let's jump into the topic and start building our own command-line tool!
Prerequisite:
A Linux VM with active RedHat Subscription.
knowledge of any scripting language like bash, go or python..etc.
So yeah that was the initial requirement for building a rpm
CTOP :
let me introduce with the ctop command, this command I have created with the bash script in a simple manner to explain it more efficiently with some basic features and in interactive way.
explanation of output:
Tells hostname, uptime, load avg and Idle percentage. [ PS: it will also tell if your cpu goes high or not so no need to calculate the idle state every time ; ) ]
showing utilization of critical fs, memory usage and obviously the CPU processes.
Get refreshed in every 2 second.
Below is the output of command:
Lets start with the technical steps :
Install the rpm dev tools packages to begin with the creation.
dnf install rpm-build rpmdevtools -y
once installation is done create a subtree from where rpm will read the content of the rpm binary that we are tying to create using below command.
[root@awx ~]# rpmdev-setuptree
[root@awx ~]# ls -lrth rpmbuild/
total 0
drwxr-xr-x. 3 root root 20 May 10 19:27 RPMS
drwxr-xr-x. 4 root root 36 May 10 19:49 BUILD
drwxr-xr-x. 2 root root 42 May 10 21:10 SOURCES
drwxr-xr-x. 2 root root 59 May 10 21:11 SPECS
drwxr-xr-x. 2 root root 128 May 10 21:11 SRPMS
drwxr-xr-x. 2 root root 6 May 10 21:11 BUILDROOT
[root@awx ~]#
Explanation of the each directory with details under the rpmbuild workspace
Directory | Purpose | Used During | Cleanup | Notes |
BUILD/ | Temporary workspace for unpacking & building | %prep , %build | Yes | Where the source code is extracted and compiled. |
BUILDROOT/ | Fake root directory for installation | %install | Yes | Files are staged here before packaging. Use %{buildroot} macro. |
RPMS/ | Final binary RPM packages (.rpm ) | Post %install | No | Output location of installable packages. |
SRPMS/ | Source RPMs (.src.rpm ) | After %prep | No | Contains spec + sources for rebuilding RPMs elsewhere. |
SOURCES/ | Tarballs, patches, and helper files | %prep | No | Input files referenced in the spec file (Source0 , Patch0 , etc.). |
SPECS/ | Spec files with build instructions | Start of build | No | Main build script (.spec ) that drives the RPM build process. |
There are several ways to create packages โ or more specifically, package your scripts โ in Linux.
For example, in this guide, I used a single Bash script to create the ctop
command. But if you want to include multiple files (like configuration files, helper scripts, etc.), it's a good idea to:
Create a dedicated directory.
Place all your required files inside that directory.
Bundle them into a tarball (
.tar.gz
) โ this makes it easier to manage and distribute as a single source archive.
As mentioned earlier, RPM builds expect sources to be placed in the ~/rpmbuild/SOURCES/
directory.
You can either:
Create your script manually using an editor like
vi
ornano
, orPlace your tarball there if you're bundling multiple files.
For our current scenario (a simple CPU monitoring script), we will:
Keep the
ctop
script inside the~/rpmbuild/SOURCES/
directory.Continue with the RPM creation from there.
[root@awx ~]# cat rpmbuild/SOURCES/ctop
############################################################################
################## AUTHOR : Mohan Juriyani #################################
############################################################################
#!/bin/bash #SHEBANG chars#####
REFRESH=2 ##########timeout of refreshment of the command
CPU_THRESHOLD=80 # % usage above which CPU is considered high
while true; do
clear
echo -e "\033[1;34m======== ctop โ A new substitue for top ========\033[0m"
echo -e "\033[1;32mHostname: $(hostname)\033[0m"
echo -e "\033[1;33mUptime: $(uptime -p)\033[0m"
CPU_IDLE=$(top -bn1 | grep "Cpu(s)" | awk -F',' '{ print $4 }' | awk '{print $1}') ##pulling out the IDLE cpu percentage##
CPU_USED=$(echo "100 - $CPU_IDLE" | bc)
echo -e "\033[1;36mCPU Load: $(uptime | awk -F'load average:' '{ print $2 }')\033[0m"
echo -e "\033[1;36mCPU Usage: $CPU_USED% (Idle: $CPU_IDLE%)\033[0m"
CPU_ALERT=$(echo "$CPU_USED > $CPU_THRESHOLD" | bc)
if [ "$CPU_ALERT" -eq 1 ]; then
echo -e "\033[1;41mWARNING: High CPU Usage!\033[0m"
else
echo -e "\033[1;41mCPU usage is normal !!\033[0m"
fi
echo -e "\n\033[1;36mMemory Usage:\033[0m"
free -h | grep -E "Mem|Swap"
echo -e "\n\033[1;36mDisk Usage:\033[0m"
df -h --output=source,size,used,avail,pcent,target | grep -v tmpfs
echo -e "\n\033[1;31mTop 5 CPU-consuming processes:\033[0m"
ps -eo pid,comm,%cpu,%mem --sort=-%cpu | head -n 6
echo -e "\n\033[1;34mTop 5 Memory-consuming processes:\033[0m"
ps -eo pid,comm,%cpu,%mem --sort=-%mem | head -n 6
echo -e "\033[1;34m============================================\033[0m"
sleep $REFRESH
done
once this is done switch to SPEC directory to create a specification of file.
There are two ways you can create it using VI editor or by using command any way if you use command you still have to edit it.
[root@awx ~]# cd ~/rpmbuild/SPECS/
[root@awx ~]# rpmdev-newspec ctop
#you will see a file like below.
[root@awx SPECS]# ls -lrth
total 12K
-rw-r--r--. 1 root root 760 May 10 21:11 ctop.spec
[root@awx SPECS]#
Once you see the spec file go ahead and edit it by mentioning the below things.
Name: ctop
Version: 1.1
Release: 1%{?dist}
Summary: Bash-based system resource monitor like top
License: MIT
URL: https://example.com/ctop
Source0: ctop
BuildArch: noarch
Requires: bash, coreutils, procps-ng, bc
%description
ctop is a simple Bash-based system resource monitoring tool that shows CPU, memory,
disk usage, and top processes, including CPU idle percentage and alerts for high CPU usage.
%prep
# Nothing to prep since this is just a script
%build
# No build step needed
%install
mkdir -p %{buildroot}/usr/local/bin
install -m 0755 %{SOURCE0} %{buildroot}/usr/local/bin/ctop
%files
/usr/local/bin/ctop
%changelog
* Sat May 10 2025 Mohan Juriyani - 1.0-1
- Initial release
๐ RPM Spec File Breakdown โ ctop.spec
Section | Purpose | Example / Notes |
Name | Name of the package | ctop |
Version | Version of the software | 1.1 |
Release | Release number, optionally includes distro macro | 1%{?dist} โ becomes 1.el8 , etc. |
Summary | Short description of the package | Shown in dnf info |
License | License under which the script is distributed | MIT |
URL | Homepage or documentation link | https://example.com/ctop |
Source0 | Main source file to be packaged | Name of the script: ctop |
BuildArch | Target architecture (e.g., noarch for scripts) | Not architecture-specific |
Requires | Runtime dependencies for the script to function | bash , coreutils , procps-ng , bc |
%description | Detailed description shown in package managers | Explains what ctop does |
%prep | Unpacks/sets up source code โ optional for scripts | Skipped here, as no tarball used |
%build | Compilation step โ not needed for scripts | Commented out |
%install | Installs script to a staged directory (%{buildroot} ) | Uses install -m 0755 to place the script in /usr/local/bin/ |
%files | Files included in the final RPM | Lists paths like /usr/local/bin/ctop |
%changelog | History of changes/releases | Good practice to update with every release |
Once you are done with writing the spec file its time to build it.
use below command to do so.
[root@awx SPECS]# rpmbuild -ba ctop.spec
setting SOURCE_DATE_EPOCH=1746835200
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.RiHbkh
+ umask 022
+ cd /root/rpmbuild/BUILD
+ RPM_EC=0
++ jobs -p
+ exit 0
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.Df0IRX
+ umask 022
+ cd /root/rpmbuild/BUILD
+ RPM_EC=0
++ jobs -p
+ exit 0
Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.a9wevN
+ umask 022
+ cd /root/rpmbuild/BUILD
+ '[' /root/rpmbuild/BUILDROOT/ctop-1.1-1.el9.x86_64 '!=' / ']'
+ rm -rf /root/rpmbuild/BUILDROOT/ctop-1.1-1.el9.x86_64
++ dirname /root/rpmbuild/BUILDROOT/ctop-1.1-1.el9.x86_64
+ mkdir -p /root/rpmbuild/BUILDROOT
+ mkdir /root/rpmbuild/BUILDROOT/ctop-1.1-1.el9.x86_64
+ mkdir -p /root/rpmbuild/BUILDROOT/ctop-1.1-1.el9.x86_64/usr/local/bin
+ install -m 0755 /root/rpmbuild/SOURCES/ctop /root/rpmbuild/BUILDROOT/ctop-1.1-1.el9.x86_64/usr/local/bin/ctop
+ '[' '%{buildarch}' = noarch ']'
+ QA_CHECK_RPATHS=1
+ case "${QA_CHECK_RPATHS:-}" in
+ /usr/lib/rpm/check-rpaths
+ /usr/lib/rpm/check-buildroot
+ /usr/lib/rpm/redhat/brp-ldconfig
+ /usr/lib/rpm/brp-compress
+ /usr/lib/rpm/brp-strip /usr/bin/strip
+ /usr/lib/rpm/brp-strip-comment-note /usr/bin/strip /usr/bin/objdump
+ /usr/lib/rpm/redhat/brp-strip-lto /usr/bin/strip
+ /usr/lib/rpm/brp-strip-static-archive /usr/bin/strip
+ /usr/lib/rpm/redhat/brp-python-bytecompile '' 1 0
+ /usr/lib/rpm/brp-python-hardlink
+ /usr/lib/rpm/redhat/brp-mangle-shebangs
mangling shebang in /usr/local/bin/ctop from /bin/bash to #!/usr/bin/bash
Processing files: ctop-1.1-1.el9.noarch
Provides: ctop = 1.1-1.el9
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires: /usr/bin/bash
Checking for unpackaged file(s): /usr/lib/rpm/check-files /root/rpmbuild/BUILDROOT/ctop-1.1-1.el9.x86_64
Wrote: /root/rpmbuild/SRPMS/ctop-1.1-1.el9.src.rpm
Wrote: /root/rpmbuild/RPMS/noarch/ctop-1.1-1.el9.noarch.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.Dnt4Ag
+ umask 022
+ cd /root/rpmbuild/BUILD
+ /usr/bin/rm -rf /root/rpmbuild/BUILDROOT/ctop-1.1-1.el9.x86_64
+ RPM_EC=0
++ jobs -p
+ exit 0
[root@awx SPECS]#
if there is any resolve it before proceeding ahead with the other step
Once your build is successfully completed you will find the rpm file inside RPMS directory so lets switch to that and install the command.
[root@awx noarch]# pwd
/root/rpmbuild/RPMS/noarch
[root@awx noarch]# ls -lrth
total 32K
-rw-r--r--. 1 root root 7.5K May 10 22:52 ctop-1.1-1.el9.noarch.rpm
[root@awx noarch]# dnf install ctop-1.1-1.el9.noarch.rpm
DONE.
Now after completing all the steps we have successfully create a rpm packages you can use the command to view your CPU and memory usage.
[root@awx noarch]# ctop
======== ctop โ A new substitue for top ========
Hostname: awx
Uptime: up 4 hours
CPU Load: 0.03, 0.03, 0.00
CPU Usage: 6.2% (Idle: 93.8%)
CPU usage is normal !!
Memory Usage:
Mem: 1.7Gi 871Mi 388Mi 1.0Mi 678Mi 902Mi
Swap: 2.0Gi 62Mi 1.9Gi
Disk Usage:
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/rhel-root 19G 14G 5.9G 70% /
/dev/sda1 1014M 285M 730M 29% /boot
Top 5 CPU-consuming processes:
PID COMMAND %CPU %MEM
5042 java 0.6 22.5
64824 kworker/0:2-cgr 0.3 0.0
63968 kworker/0:3-eve 0.1 0.0
1 systemd 0.0 0.9
2 kthreadd 0.0 0.0
Top 5 Memory-consuming processes:
PID COMMAND %CPU %MEM
5042 java 0.6 22.5
814 containerd 0.0 2.7
1224 dockerd 0.0 2.5
48492 python3 0.0 2.0
786 tuned 0.0 1.5
============================================
Thanks for bearing with me till here !
Hope you have learn something new.
Key- takeaways :
Workspace directory should have correct permission and you should have sudo privileges.
Incase if your command or binary or rpm having different files make sure you create tarball and then create spec file and all.
Subscribe to my newsletter
Read articles from Mohan Juriyani directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
