Cómo crear un paquete .rpm paso a paso

Última actualización: 10/12/2025
Autor: Isaac
  • Para crear un RPM necesitas un árbol de trabajo rpmbuild, un archivo SPEC bien definido y las fuentes organizadas en SOURCES.
  • Las secciones %prep, %build, %install y %files del SPEC controlan todo el ciclo de desempaquetar, compilar, instalar en buildroot y seleccionar qué se incluye.
  • rpmbuild genera tanto paquetes binarios como fuente, que puedes instalar, inspeccionar y mantener con las mismas herramientas que usas para paquetes oficiales.
  • Firmar los RPM con GPG y verificar sus firmas en los sistemas destino garantiza la integridad y autoría de los paquetes distribuidos.

Guía para crear paquetes RPM paso a paso

Si trabajas con distribuciones como RHEL, CentOS, Fedora u otras derivadas de Red Hat, tarde o temprano te tocará preguntarte cómo crear tu propio paquete .rpm paso a paso. Tener tus aplicaciones empaquetadas te permite instalarlas, actualizarlas y distribuirlas de forma limpia, controlada y automatizable, igual que haces con cualquier paquete del repositorio oficial.

Puede sonar a «magia negra», pero verás que construir RPMs es un proceso muy lógico: se basa en preparar un árbol de trabajo, escribir un archivo SPEC con las instrucciones de empaquetado y dejar que la herramienta rpmbuild haga el trabajo duro. A partir de ahí, puedes generar paquetes simples (un script y poco más) o auténticos «paquetones» con binarios, librerías, documentación, configuración, dependencias… lo que necesites.

Qué es un paquete RPM y por qué merece la pena crearlos

Un paquete RPM no es más que un archivo que contiene ficheros de tu software junto a metadatos: nombre, versión, arquitectura, dependencias, scripts de instalación, etc. Internamente, los datos se almacenan en un contenedor cpio comprimido con herramientas como gzip, bzip2, XZ, LZMA o XAR, según la versión de RPM que estés usando.

El nombre de un paquete RPM suele tener esta forma: nombre-versión-release.arquitectura.rpm. Por ejemplo, nginx-1.19.3-1.el7.ngx.x86_64.rpm, donde se distinguen cuatro elementos básicos: el nombre del programa, su versión, el número de release del empaquetado y la arquitectura para la que se ha construido (x86_64, noarch, etc.).

Crear tus propios RPM te da varias ventajas muy claras: puedes automatizar instalaciones idénticas en muchos servidores, gestionar dependencias con el mismo sistema que ya usas (yum, dnf, zypper) y conocer los instaladores de paquetes más populares, controlar qué se instala y dónde, firmar paquetes para garantizar su autoría y, si quieres ir un paso más allá, montar tu repositorio RPM corporativo.

Preparar el entorno y las herramientas necesarias

Antes de ponerte a empaquetar nada, necesitas tener instaladas las herramientas de desarrollo mínimas y los paquetes relacionados con rpm-build. Esto cambia un poco según la distribución, pero la idea general es la misma.

En sistemas basados en Red Hat (RHEL, CentOS, Rocky, Alma, Fedora anterior a dnf), lo habitual es instalar al menos:

  • rpm-build y rpmdevtools para poder usar rpmbuild y utilidades auxiliares.
  • Paquetes de compilación: gcc, make (automatizar la compilación con Makefile), glibc-devel, automake, autoconf, etc., si vas a construir desde código fuente.
  • Otros paquetes de desarrollo específicos (por ejemplo, openssl-devel, zlib-devel, pcre-devel en el caso de Nginx).

En una máquina RHEL/CentOS/derivada moderna puedes tirar de:

yum install -y rpm-build rpmdevtools gcc make glibc-devel automake autoconf

Si estás en Fedora, además de las herramientas de desarrollo es habitual instalar el grupo de herramientas de compilación y el paquete fedora-packager:

yum install @development-tools
yum install fedora-packager

En Debian o Ubuntu también se pueden generar RPMs, aunque no sea su formato nativo. Allí necesitas, como mínimo, rpm y rpm2cpio para manipular y construir paquetes:

sudo apt -y install rpm rpm2cpio

En entornos de escritorio como algunas distros derivadas, si quieres firmar tus paquetes tendrás que añadir además gnupg y, si te apetece algo gráfico, herramientas como seahorse y gpgme para gestionar las claves GPG de forma cómoda.

Crear un usuario y el árbol rpmbuild de trabajo

Hay una regla de oro cuando se construyen paquetes: jamás compiles o empaquetes como root. Si en un SPEC hay una ruta mal puesta o un script con efectos colaterales, puedes liar una buena si eso se ejecuta con privilegios de superusuario.

  SDesk, Zorin OS y Linuxfx: comparativa definitiva como alternativas a Windows 11

Lo recomendable es crear un usuario específico para empaquetar, por ejemplo rpm o makerpm, y usarlo siempre para trabajar con rpmbuild. En RHEL/CentOS, como root, podrías hacer algo así:

useradd rpm -m
passwd rpm

En Fedora, si vas a usar mock (entorno aislado de construcción, por ejemplo con contenedores Docker), es buena idea meter al usuario en el grupo mock para poder hacer builds limpios:

/usr/sbin/useradd makerpm
usermod -a -G mock makerpm

Una vez que has iniciado sesión con ese usuario, el siguiente paso es crear la estructura de directorios estándar que usa rpmbuild. En Red Hat y Fedora basta con ejecutar:

rpmdev-setuptree

Este comando creará en tu home la carpeta rpmbuild con varios subdirectorios esenciales:

  • BUILD: donde se descomprimen las fuentes y se compila el código.
  • BUILDROOT (o BUILDROOT bajo BUILD): donde se instala de forma simulada el contenido del paquete.
  • SOURCES: donde se colocan los archivos de código fuente, tarballs y parches.
  • SPECS: donde vivirán los archivos .spec con las instrucciones de empaquetado.
  • RPMS: destino de los paquetes binarios generados, organizado por arquitecturas (x86_64, noarch, etc.).
  • SRPMS: contenedor de los paquetes fuente .src.rpm.

Si estás en Debian y no tienes rpmdevtools, puedes montar algo equivalente a mano con:

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

Organizar las fuentes en SOURCES con un ejemplo práctico

Con el árbol listo, toca preparar el contenido que quieres empaquetar. Lo típico es tener un directorio con tu programa o script y luego crear un tar.gz de ese directorio para colocarlo en ~/rpmbuild/SOURCES.

Imagina que quieres empaquetar un script llamado n (gestor de notas en terminal) o un pequeño ejemplo llamado test-package-1.0 que contiene un README y un script ejecutable. En ese caso, en tu directorio de trabajo tendrías algo así:

test-package-1.0/
README.txt
test-check
configure

Una curiosidad: aunque no vayas a compilar nada, muchas plantillas de SPEC ejecutan ./configure en la fase %build. Si ese archivo no existe, obtendrás un error. Para evitarlo, se suele crear un configure vacío y darle permisos de ejecución:

touch configure
chmod 0744 configure

Cuando tengas el contenido listo, creas el tarball y lo copias a SOURCES:

tar -czvf test-package-1.0.tar.gz test-package-1.0/
cp -p test-package-1.0.tar.gz ~/rpmbuild/SOURCES/

En otros casos, como el script de notas «n» o la fuente tipográfica Vremena, clonas directamente desde un repositorio git dentro de SOURCES, ajustas el nombre de carpeta (por ejemplo, n-fanta-0.0.1), eliminas el directorio .git para no meter el historial en el tar.gz y por último generas el archivo comprimido:

tar -czvf n-fanta-0.0.1.tar.gz n-fanta-0.0.1

El archivo SPEC: el corazón del paquete RPM

El archivo .spec es donde se define todo lo relativo al paquete: nombre, versión, licencia, dónde están las fuentes, cómo se compila, cómo se instala, qué ficheros se incluyen y qué scripts se ejecutan al instalar o desinstalar.

Normalmente, los SPEC se guardan en ~/rpmbuild/SPECS y conviene que su nombre coincida con el del paquete, por ejemplo test-package.spec o vremena-font.spec. Puedes crear una plantilla mínima con:

cd ~/rpmbuild/SPECS
rpmdev-newspec test-package

Esto generará un esqueleto llamado test-package.spec o newpackage.spec, que luego puedes adaptar. En Debian, si no dispones de rpmdev-newspec, puedes crear tu propia plantilla.spec a mano y reutilizarla copiándola cada vez que empieces un paquete nuevo.

Un SPEC típico empieza con una zona de cabecera donde se indican datos básicos:

  • Name: nombre base del paquete (en minúsculas y sin espacios).
  • Version: versión del software, normalmente la misma que usa el autor.
  • Release: número de build de tu empaquetado, muchas veces 1%{?dist}.
  • Summary: descripción corta en una línea, sin punto final.
  • License: tipo de licencia (GPLv3, MIT, etc.).
  • URL: página principal del proyecto.
  • Source0: ruta o URL del tarball principal (suele incluir %{name} y %{version}).
  • BuildRequires: paquetes necesarios para compilar.
  • Requires: dependencias que debe tener instaladas el sistema al usar el RPM.
  QEMU 10: Novedades, rendimiento y compatibilidad revolucionaria

A partir de ahí, aparecen una serie de secciones delimitadas por macros que empiezan por %, cada una con un propósito claro:

  • %description: texto más largo explicando qué hace el paquete.
  • %prep: preparación de fuentes (desempaquetado, aplicación de parches).
  • %build: compilación o tareas equivalentes.
  • %check: tests automáticos, si los hay.
  • %install: instalación en el árbol de %{buildroot} de forma simulada.
  • %files: lista de archivos y directorios que formarán parte del paquete final.
  • %changelog: histórico de cambios del SPEC.

Dentro del SPEC puedes usar muchas macros RPM que se expanden automáticamente: %{name}, %{version}, %{_bindir} (normalmente /usr/bin), %{_sysconfdir} (/etc), %{_libdir} (/usr/lib o /usr/lib64), %{_datadir} (/usr/share), etc. Esto facilita mantener el SPEC a lo largo del tiempo y entre distintas arquitecturas.

Fases %prep, %build y %check en detalle

La sección %prep suele empezar con una macro %setup, que desempaqueta la fuente principal desde SOURCE0 en el directorio de BUILD. Lo más típico es:

%prep
%setup -q

Si el tarball se descomprime en un directorio que no coincide con el nombre por defecto, puedes indicar el nombre explícitamente con -n, por ejemplo %setup -q -n n-fanta-0.0.1. Aquí también se aplican los parches declarados como Patch0, Patch1… mediante macros %patch con el nivel adecuado (-p1, etc.).

En la sección %build es donde se realiza la compilación o el equivalente. Muchos proyectos basados en autotools admiten simplemente:

%build
%configure
make %{?_smp_mflags}

En proyectos más simples, como el ejemplo de test-package que sólo contiene un script, podrías incluso dejar la sección vacía o comentar las líneas de %configure y make si no son necesarias, siempre y cuando no se ejecuten en el flujo de rpmbuild.

La sección %check se reserva para lanzar baterías de tests automáticos, normalmente con comandos tipo make test o make check. Conviene tenerla separada de %build para poder desactivar las pruebas cuando haga falta (por ejemplo, en builds rápidos o en entornos limitados).

Instalar en el buildroot y declarar los ficheros del paquete

La sección %install es donde se simula la instalación del software en un árbol de directorios vacío llamado %{buildroot}. Lo normal es limpiar primero restos anteriores y luego usar el clásico patrón de DESTDIR:

%install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}

Si tu proyecto de ejemplo es muy sencillo y no tiene make install, puedes recurrir a comandos install y mkdir para crear directorios y copiar ficheros a mano, como en el caso de test-package:

%install
rm -rf $RPM_BUILD_ROOT
install -d -m 0755 /var/tmp/test-package
install -m 0644 README.txt /var/tmp/test-package
install -m 0755 test-check /var/tmp/test-package

En paquetes más elaborados, como el de la fuente vremena-font, la sección %install se encarga de colocar los archivos en rutas estándar del sistema, por ejemplo bajo /usr/share/fonts, preservando permisos apropiados y dejando todo listo para que luego se actualice la caché de fuentes con un script %post.

Una vez que has llenado el buildroot con lo necesario, llega el momento de enumerar qué se va a empaquetar en la sección %files. Aquí se usan rutas relativas a %{buildroot}, macros y algunos prefijos especiales:

  • %doc para marcar documentación (README, LICENSE, etc.).
  • %config o %config(noreplace) para archivos de configuración bajo /etc.
  • %lang para indicar ficheros de localización por idioma.

En el caso de la fuente Vremena, por ejemplo, la sección %files listaría cada archivo .otf bajo /usr/share/fonts/vremena-serif/, mientras que en paquetes sencillos basta con incluir el directorio donde has instalado el script o los ficheros de datos.

Si tu paquete instala librerías compartidas en rutas estándar del sistema (por ejemplo, en %{_libdir}), se suele añadir en el SPEC un par de scriptlets %post y %postun que ejecutan ldconfig para actualizar la caché del enlazador dinámico después de instalar o desinstalar.

  The best way to Rearrange and Take away Icons From Mac Menu Bar

Construir el paquete RPM: binario y fuente

Con el SPEC listo y el tar.gz en SOURCES, ya puedes invocar rpmbuild para generar tu paquete. Lo habitual es cambiar al directorio SPECS y lanzar:

cd ~/rpmbuild/SPECS
rpmbuild -ba test-package.spec

La opción -ba indica que quieres construir tanto el RPM binario como el .src.rpm. Si sólo quieres el binario, puedes usar -bb; si lo que te interesa es únicamente el source RPM, -bs. Durante el proceso verás cómo se ejecutan las fases %prep, %build, %install, etc. y, si todo va bien, la salida terminará con algo como exit 0.

El resultado quedará alojado en:

  • ~/rpmbuild/RPMS/<arquitectura>/ para el RPM binario, por ejemplo test-package-1.0-1.el6.i386.rpm o vremena-font-0.1-1.x86_64.rpm.
  • ~/rpmbuild/SRPMS/ para el paquete fuente correspondiente.

Si estás empaquetando algo más complejo como Nginx con un módulo SPNEGO, la lógica es exactamente la misma, aunque en ese caso podrías partir de un .src.rpm oficial de nginx, instalarlo con rpm -Uvh, modificar el SPEC generado, añadir el módulo dinámico en la sección de build y generar nuevos RPM con rpmbuild -bb nginx.spec.

Firmar y verificar paquetes RPM con GPG

Si vas a distribuir tus RPM a otras máquinas (o incluso a toda la organización), es muy recomendable firmarlos digitalmente con GPG. La firma permite que los sistemas de destino verifiquen que el paquete realmente lo has construido tú y que no ha sido modificado en tránsito.

El flujo básico es: generas una clave GPG, configuras tus macros de rpm para usarla, firmas el RPM y luego distribuyes la clave pública a los clientes para que puedan validar la firma. Un ejemplo de configuración mínima en ~/.rpmmacros sería:

%_signature gpg
%_gpg_path %(echo "$HOME")/.gnupg
%_gpg_name Nombre Apellido (Comentario) <correo@dominio>
%_gpgbin /usr/bin/gpg2

Una vez creada la clave con gpg2 –gen-key (elige tipo RSA, sin caducidad si lo prefieres, y una passphrase robusta), puedes firmar un paquete ya construido con:

rpm --addsign rpmbuild/RPMS/x86_64/mi-paquete-1.0-1.x86_64.rpm

Al ejecutar este comando te pedirá la contraseña de la clave GPG. Para ver qué claves privadas tiene rpm disponibles puedes comprobarlo con gpg -K. Y si quieres ser fino, en ~/.rpmmacros puedes ajustar la macro %__gpg_sign_cmd para afinar los parámetros de la firma (algoritmo de hash, modo batch, etc.).

En la máquina de destino, antes de verificar la firma tendrás que importar la clave pública del firmante. Eso se hace exportando la clave en el origen:

gpg2 -a --export Nombre > RPM-GPG-KEY-Nombre

y luego, como root en el destino:

rpm --import RPM-GPG-KEY-Nombre

A partir de ahí puedes chequear la firma de un paquete con:

rpm --checksig mi-paquete-1.0-1.x86_64.rpm

Si todo está correcto, verás un mensaje indicando que los digests y las firmas son válidos, algo del estilo digests signatures OK. Esto te da una garantía bastante sólida de integridad y autoría.

El proceso de aprender a crear, instalar, inspeccionar, firmar y verificar RPMs convierte lo que parece un mundo complejo en algo bastante controlable: dominas dónde van tus ficheros, qué dependencias declaras, cómo automatizas la instalación y cómo respaldar todo eso con firmas criptográficas, lo que te permite integrar tus propios paquetes en el mismo flujo de trabajo que cualquier software oficial de la distribución.

Crear entornos de desarrollo con WSL2 + Docker + VSCode
Artículo relacionado:
Guía completa para crear entornos con WSL2 + Docker + VS Code