Creando paquetes .rpm para la familia Red Hat

Mascote do LinuxPro colocando o software dentro de uma caixa marcada com o chapéu vermelho, na esteira de empacotamento

En la primera parte de este dúo montamos paquetes .deb para Debian y Ubuntu. Ahora le toca a la familia Red Hat — RHEL, Fedora, Rocky, AlmaLinux, y por extensión openSUSE: vamos a crear paquetes .rpm desde cero, desde el archivo spec hasta un repositorio dnf propio, pasando por rpmbuild, rpmlint, mock y firma GPG.

Cómo funciona RPM

A diferencia del .deb (un directorio que montas a mano), el mundo RPM gira en torno a un único archivo-receta: el spec file. describe los metadatos, las dependencias, cómo compilar, qué instalar y el changelog, y el RPM rpmbuild hace el resto. Para inspeccionar cualquier paquete:

rpm -qip htop-*.rpm      # metadados
rpm -qlp htop-*.rpm      # lista de arquivos
rpm -q --scripts htop    # scripts de instalação de um pacote instalado

Preparando el banco de trabajo

# Fedora / RHEL / Rocky / Alma
sudo dnf install rpm-build rpmdevtools rpmlint

# cria a árvore de trabalho em ~/rpmbuild
rpmdev-setuptree
tree ~/rpmbuild
# BUILD/ BUILDROOT/ RPMS/ SOURCES/ SPECS/ SRPMS/

Regla de oro: nunca ejecutes rpmbuild como root: un %install mal escrito puede escribir en el sistema de verdad.

El archivo spec, sección por sección

Genera un esqueleto y vamos a rellenarlo (meuapp de nuevo como ejemplo):

cd ~/rpmbuild/SPECS
rpmdev-newspec meuapp
Name:           meuapp
Version:        1.0.0
Release:        1%{?dist}
Summary:        Utilitário de exemplo do LinuxPro

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

BuildRequires:  gcc, make
Requires:       ca-certificates

%description
Descrição longa do pacote. Pode ter vários
parágrafos, sem a regra do espaço do Debian.

%prep
%setup -q                # desempacota o Source0 em BUILD/

%build
make %{?_smp_mflags}     # compila com paralelismo

%install
rm -rf %{buildroot}
install -D -m 0755 meuapp %{buildroot}%{_bindir}/meuapp
install -D -m 0644 meuapp.conf %{buildroot}%{_sysconfdir}/meuapp/meuapp.conf
install -D -m 0644 meuapp.1 %{buildroot}%{_mandir}/man1/meuapp.1

%files
%license LICENSE
%{_bindir}/meuapp
%config(noreplace) %{_sysconfdir}/meuapp/meuapp.conf
%{_mandir}/man1/meuapp.1*

%changelog
* Sat Sep 06 2026 Nilton Oliveira  - 1.0.0-1
- Primeira versao do pacote

Puntos que vale la pena destacar:

  • %{?dist} en Release se convierte .el9, .fc42 etc.: el mismo spec sirve para varias distros
  • %config(noreplace) es el conffiles del mundo RPM: la actualización no sobrescribe la configuración editada (la nueva llega como .rpmnew)
  • Utilice siempre las macros de ruta (%{_bindir}, %{_sysconfdir}, %{_unitdir}) en lugar de rutas fijas
  • Todo lo que el %install crear necesita estar listado en %files — los sobrantes provocan error de compilación (a propósito)

Construyendo el paquete

# fonte na pasta certa
tar czf ~/rpmbuild/SOURCES/meuapp-1.0.0.tar.gz meuapp-1.0.0/

# build completo: binário + source RPM
rpmbuild -ba ~/rpmbuild/SPECS/meuapp.spec

ls ~/rpmbuild/RPMS/x86_64/   # meuapp-1.0.0-1.el9.x86_64.rpm
ls ~/rpmbuild/SRPMS/         # meuapp-1.0.0-1.el9.src.rpm

Instale y valide con rpmlint (el “lintian” del RPM):

rpmlint ~/rpmbuild/RPMS/x86_64/meuapp-*.rpm
sudo dnf install ~/rpmbuild/RPMS/x86_64/meuapp-1.0.0-1.el9.x86_64.rpm
rpm -ql meuapp
sudo dnf remove meuapp

Scripts de instalación y usuario de sistema

Los equivalentes de postinst/prerm son %pre, %post, %preun e %postun. Un usuario del sistema, por ejemplo:

%pre
getent passwd meuapp >/dev/null || 
  useradd --system --no-create-home --shell /sbin/nologin meuapp
exit 0

Los servicios systemd tienen macros listas: instale la unit en %{_unitdir} y use:

BuildRequires: systemd-rpm-macros

%post
%systemd_post meuapp.service

%preun
%systemd_preun meuapp.service

%postun
%systemd_postun_with_restart meuapp.service

Dependencias automáticas

El RPM analiza los binarios por sí solo: las bibliotecas enlazadas se convierten en Requires automáticamente (ej.: libssl.so.3()(64bit)). Tú declaras a mano solo lo que él no ve: binarios invocados mediante exec, archivos de datos, etc. Los binarios estáticos de Go/Rust pasan casi sin dependencias.

Compilación limpia con mock

Compilar en tu máquina “sucia” oculta dependencias de compilación faltantes. El mock construye el paquete en un chroot desechable de la distro objetivo: así es como Fedora/EPEL compila todo:

sudo dnf install mock
sudo usermod -aG mock $USER && newgrp mock
mock -r rocky-9-x86_64 ~/rpmbuild/SRPMS/meuapp-1.0.0-1.el9.src.rpm
# resultado em /var/lib/mock/rocky-9-x86_64/result/

Con un SRPM y mock, generas el mismo paquete para Rocky 9, Fedora 42 y openSUSE cambiando solo el -r.

Firma GPG

gpg --gen-key   # se ainda não tem chave
echo "%_gpg_name Nilton Oliveira " >> ~/.rpmmacros
rpm --addsign ~/rpmbuild/RPMS/x86_64/meuapp-*.rpm
rpm -K ~/rpmbuild/RPMS/x86_64/meuapp-*.rpm   # verifica

Repositorio dnf propio

sudo dnf install createrepo_c
mkdir -p /srv/repo/el9/x86_64
cp ~/rpmbuild/RPMS/x86_64/*.rpm /srv/repo/el9/x86_64/
createrepo_c /srv/repo/el9/x86_64/

Sírvelo con nginx y, en el cliente, un .repo resuelve:

cat | sudo tee /etc/yum.repos.d/meurepo.repo << 'EOF'
[meurepo]
name=Meu Repositorio
baseurl=https://repo.example.com/el9/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://repo.example.com/chave.gpg
EOF
sudo dnf install meuapp

¿Has publicado un paquete nuevo? Ejecuta createrepo_c --update y los clientes lo reciben mediante dnf upgrade.

Atajo: fpm

El mismo fpm del post anterior también escupe un RPM: para una herramienta interna, se resuelve con un comando:

fpm -s dir -t rpm -n meuapp -v 1.0.0 --description "Meu utilitário" 
  --depends ca-certificates meuapp=/usr/bin/meuapp

Resumen práctico

  • ¿Herramienta interna? fpm o un spec mínimo: rápido y suficiente
  • ¿Publicando de verdad? spec completo + rpmlint limpio + build en el mock + RPM firmado + repositorio con createrepo_c
  • Config con %config(noreplace), systemd con las macros %systemd_*, rutas siempre vía macros

Con las dos entradas de la pareja — .deb y .rpm — cubres prácticamente cualquier servidor Linux en producción. ¿Dudas o un caso complicado de empaquetado? Llámame al página de contacto.