
Na primeira parte desta dupla montamos pacotes .deb para Debian e Ubuntu. Agora é a vez da família Red Hat — RHEL, Fedora, Rocky, AlmaLinux, e por tabela openSUSE: vamos criar pacotes .rpm do zero, do spec file ao repositório dnf próprio, passando por rpmbuild, rpmlint, mock e assinatura GPG.
Como o RPM funciona
Diferente do .deb (um diretório que você monta na mão), o mundo RPM gira em torno de um único arquivo-receita: o spec file. Ele descreve metadados, dependências, como compilar, o que instalar e o changelog — e o rpmbuild faz o resto. Para inspecionar qualquer pacote:
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 a bancada
# 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/
Regra de ouro: nunca rode rpmbuild como root — um %install mal escrito pode escrever no sistema de verdade.
O spec file, seção por seção
Gere um esqueleto e vamos preencher (meuapp de novo como exemplo):
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
Pontos que valem destacar:
%{?dist}no Release vira.el9,.fc42etc. — o mesmo spec serve para várias distros%config(noreplace)é oconffilesdo mundo RPM: upgrade não sobrescreve a config editada (a nova chega como.rpmnew)- Use sempre as macros de caminho (
%{_bindir},%{_sysconfdir},%{_unitdir}) em vez de caminhos fixos - Tudo que o
%installcriar precisa estar listado em%files— sobras causam erro de build (de propósito)
Construindo o pacote
# 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 e valide com o rpmlint (o “lintian” do 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 instalação e usuário de sistema
Os equivalentes do postinst/prerm são %pre, %post, %preun e %postun. Um usuário de sistema, por exemplo:
%pre
getent passwd meuapp >/dev/null ||
useradd --system --no-create-home --shell /sbin/nologin meuapp
exit 0
Serviço systemd tem macros prontas — instale a unit em %{_unitdir} e use:
BuildRequires: systemd-rpm-macros
%post
%systemd_post meuapp.service
%preun
%systemd_preun meuapp.service
%postun
%systemd_postun_with_restart meuapp.service
Dependências automáticas
O RPM escaneia os binários sozinho: bibliotecas linkadas viram Requires automaticamente (ex.: libssl.so.3()(64bit)). Você declara na mão só o que ele não enxerga — binários chamados via exec, arquivos de dados, etc. Binários Go/Rust estáticos passam quase sem dependências.
Build limpo com mock
Compilar na sua máquina “suja” esconde dependências de build faltantes. O mock constrói o pacote num chroot descartável da distro-alvo — é assim que o Fedora/EPEL constrói tudo:
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/
Com um SRPM e o mock, você gera o mesmo pacote para Rocky 9, Fedora 42 e openSUSE trocando só o -r.
Assinatura 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
Repositório dnf próprio
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/
Sirva com nginx e, no cliente, um .repo resolve:
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
Publicou pacote novo? Rode createrepo_c --update e os clientes recebem via dnf upgrade.
Atalho: fpm
O mesmo fpm do post anterior também cospe RPM — para ferramenta interna, resolve em um comando:
fpm -s dir -t rpm -n meuapp -v 1.0.0 --description "Meu utilitário"
--depends ca-certificates meuapp=/usr/bin/meuapp
Resumo prático
- Ferramenta interna?
fpmou um spec mínimo — rápido e suficiente - Publicando de verdade? spec completo +
rpmlintlimpo + build nomock+ RPM assinado + repositório comcreaterepo_c - Config com
%config(noreplace), systemd com as macros%systemd_*, caminhos sempre via macros
Com os dois posts da dupla — .deb e .rpm — você cobre praticamente todo servidor Linux em produção. Dúvidas ou um caso cabeludo de empacotamento? Me chame na página de contato.