{"id":692,"date":"2026-09-06T14:47:34","date_gmt":"2026-09-06T17:47:34","guid":{"rendered":"https:\/\/www.linuxpro.com.br\/2026\/09\/criando-pacotes-rpm\/"},"modified":"2026-09-08T09:03:57","modified_gmt":"2026-09-08T12:03:57","slug":"criando-pacotes-rpm","status":"publish","type":"post","link":"https:\/\/www.linuxpro.com.br\/en\/2026\/09\/criando-pacotes-rpm\/","title":{"rendered":"Creating .rpm packages for the Red Hat family"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" alt=\"Mascote do LinuxPro colocando o software dentro de uma caixa marcada com o chap\u00e9u vermelho, na esteira de empacotamento\" src=\"\/wp-content\/uploads\/2026\/09\/pacotes-rpm-v3.webp\" width=\"1486\" height=\"856\" \/><\/p>\n<p>Na <a href=\"\/2026\/09\/criando-pacotes-deb\/\">primeira parte desta dupla<\/a> montamos pacotes .deb para Debian e Ubuntu. Agora \u00e9 a vez da fam\u00edlia Red Hat \u2014 RHEL, Fedora, Rocky, AlmaLinux, e por tabela openSUSE: vamos criar pacotes <strong>.rpm<\/strong> do zero, do spec file ao reposit\u00f3rio <code>dnf<\/code> pr\u00f3prio, passando por rpmbuild, rpmlint, mock e assinatura GPG.<\/p>\n<h2>Como o RPM funciona<\/h2>\n<p>Diferente do .deb (um diret\u00f3rio que voc\u00ea monta na m\u00e3o), o mundo RPM gira em torno de um \u00fanico arquivo-receita: o <strong>spec file<\/strong>. Ele descreve metadados, depend\u00eancias, como compilar, o que instalar e o changelog \u2014 e o <code>rpmbuild<\/code> faz o resto. Para inspecionar qualquer pacote:<\/p>\n<pre><code class=\"language-bash\">rpm -qip htop-*.rpm      # metadados\nrpm -qlp htop-*.rpm      # lista de arquivos\nrpm -q --scripts htop    # scripts de instala\u00e7\u00e3o de um pacote instalado<\/code><\/pre>\n<h2>Preparando a bancada<\/h2>\n<pre><code class=\"language-bash\"># Fedora \/ RHEL \/ Rocky \/ Alma\nsudo dnf install rpm-build rpmdevtools rpmlint\n\n# cria a \u00e1rvore de trabalho em ~\/rpmbuild\nrpmdev-setuptree\ntree ~\/rpmbuild\n# BUILD\/ BUILDROOT\/ RPMS\/ SOURCES\/ SPECS\/ SRPMS\/<\/code><\/pre>\n<p>Regra de ouro: <strong>nunca<\/strong> rode <code>rpmbuild<\/code> como root \u2014 um <code>%install<\/code> mal escrito pode escrever no sistema de verdade.<\/p>\n<h2>O spec file, se\u00e7\u00e3o por se\u00e7\u00e3o<\/h2>\n<p>Gere um esqueleto e vamos preencher (<code>meuapp<\/code> de novo como exemplo):<\/p>\n<pre><code class=\"language-bash\">cd ~\/rpmbuild\/SPECS\nrpmdev-newspec meuapp<\/code><\/pre>\n<pre><code class=\"language-bash\">Name:           meuapp\nVersion:        1.0.0\nRelease:        1%{?dist}\nSummary:        Utilit\u00e1rio de exemplo do LinuxPro\n\nLicense:        MIT\nURL:            https:\/\/www.linuxpro.com.br\nSource0:        %{name}-%{version}.tar.gz\n\nBuildRequires:  gcc, make\nRequires:       ca-certificates\n\n%description\nDescri\u00e7\u00e3o longa do pacote. Pode ter v\u00e1rios\npar\u00e1grafos, sem a regra do espa\u00e7o do Debian.\n\n%prep\n%setup -q                # desempacota o Source0 em BUILD\/\n\n%build\nmake %{?_smp_mflags}     # compila com paralelismo\n\n%install\nrm -rf %{buildroot}\ninstall -D -m 0755 meuapp %{buildroot}%{_bindir}\/meuapp\ninstall -D -m 0644 meuapp.conf %{buildroot}%{_sysconfdir}\/meuapp\/meuapp.conf\ninstall -D -m 0644 meuapp.1 %{buildroot}%{_mandir}\/man1\/meuapp.1\n\n%files\n%license LICENSE\n%{_bindir}\/meuapp\n%config(noreplace) %{_sysconfdir}\/meuapp\/meuapp.conf\n%{_mandir}\/man1\/meuapp.1*\n\n%changelog\n* Sat Sep 06 2026 Nilton Oliveira <nilton@example.com> - 1.0.0-1\n- Primeira versao do pacote<\/code><\/pre>\n<p>Pontos que valem destacar:<\/p>\n<ul>\n<li><code>%{?dist}<\/code> no Release vira <code>.el9<\/code>, <code>.fc42<\/code> etc. \u2014 o mesmo spec serve para v\u00e1rias distros<\/li>\n<li><code>%config(noreplace)<\/code> \u00e9 o <code>conffiles<\/code> do mundo RPM: upgrade n\u00e3o sobrescreve a config editada (a nova chega como <code>.rpmnew<\/code>)<\/li>\n<li>Use sempre as macros de caminho (<code>%{_bindir}<\/code>, <code>%{_sysconfdir}<\/code>, <code>%{_unitdir}<\/code>) em vez de caminhos fixos<\/li>\n<li>Tudo que o <code>%install<\/code> criar <strong>precisa<\/strong> estar listado em <code>%files<\/code> \u2014 sobras causam erro de build (de prop\u00f3sito)<\/li>\n<\/ul>\n<h2>Construindo o pacote<\/h2>\n<pre><code class=\"language-bash\"># fonte na pasta certa\ntar czf ~\/rpmbuild\/SOURCES\/meuapp-1.0.0.tar.gz meuapp-1.0.0\/\n\n# build completo: bin\u00e1rio + source RPM\nrpmbuild -ba ~\/rpmbuild\/SPECS\/meuapp.spec\n\nls ~\/rpmbuild\/RPMS\/x86_64\/   # meuapp-1.0.0-1.el9.x86_64.rpm\nls ~\/rpmbuild\/SRPMS\/         # meuapp-1.0.0-1.el9.src.rpm<\/code><\/pre>\n<p>Instale e valide com o rpmlint (o &#8220;lintian&#8221; do RPM):<\/p>\n<pre><code class=\"language-bash\">rpmlint ~\/rpmbuild\/RPMS\/x86_64\/meuapp-*.rpm\nsudo dnf install ~\/rpmbuild\/RPMS\/x86_64\/meuapp-1.0.0-1.el9.x86_64.rpm\nrpm -ql meuapp\nsudo dnf remove meuapp<\/code><\/pre>\n<h2>Scripts de instala\u00e7\u00e3o e usu\u00e1rio de sistema<\/h2>\n<p>Os equivalentes do postinst\/prerm s\u00e3o <code>%pre<\/code>, <code>%post<\/code>, <code>%preun<\/code> e <code>%postun<\/code>. Um usu\u00e1rio de sistema, por exemplo:<\/p>\n<pre><code class=\"language-bash\">%pre\ngetent passwd meuapp >\/dev\/null || \n  useradd --system --no-create-home --shell \/sbin\/nologin meuapp\nexit 0<\/code><\/pre>\n<p>Servi\u00e7o systemd tem macros prontas \u2014 instale a unit em <code>%{_unitdir}<\/code> e use:<\/p>\n<pre><code class=\"language-bash\">BuildRequires: systemd-rpm-macros\n\n%post\n%systemd_post meuapp.service\n\n%preun\n%systemd_preun meuapp.service\n\n%postun\n%systemd_postun_with_restart meuapp.service<\/code><\/pre>\n<h2>Depend\u00eancias autom\u00e1ticas<\/h2>\n<p>O RPM escaneia os bin\u00e1rios sozinho: bibliotecas linkadas viram <code>Requires<\/code> automaticamente (ex.: <code>libssl.so.3()(64bit)<\/code>). Voc\u00ea declara na m\u00e3o s\u00f3 o que ele n\u00e3o enxerga \u2014 bin\u00e1rios chamados via <code>exec<\/code>, arquivos de dados, etc. Bin\u00e1rios Go\/Rust est\u00e1ticos passam quase sem depend\u00eancias.<\/p>\n<h2>Build limpo com mock<\/h2>\n<p>Compilar na sua m\u00e1quina &#8220;suja&#8221; esconde depend\u00eancias de build faltantes. O <code>mock<\/code> constr\u00f3i o pacote num chroot descart\u00e1vel da distro-alvo \u2014 \u00e9 assim que o Fedora\/EPEL constr\u00f3i tudo:<\/p>\n<pre><code class=\"language-bash\">sudo dnf install mock\nsudo usermod -aG mock $USER && newgrp mock\nmock -r rocky-9-x86_64 ~\/rpmbuild\/SRPMS\/meuapp-1.0.0-1.el9.src.rpm\n# resultado em \/var\/lib\/mock\/rocky-9-x86_64\/result\/<\/code><\/pre>\n<p>Com um SRPM e o mock, voc\u00ea gera o mesmo pacote para Rocky 9, Fedora 42 e openSUSE trocando s\u00f3 o <code>-r<\/code>.<\/p>\n<h2>Assinatura GPG<\/h2>\n<pre><code class=\"language-bash\">gpg --gen-key   # se ainda n\u00e3o tem chave\necho \"%_gpg_name Nilton Oliveira <nilton@example.com>\" >> ~\/.rpmmacros\nrpm --addsign ~\/rpmbuild\/RPMS\/x86_64\/meuapp-*.rpm\nrpm -K ~\/rpmbuild\/RPMS\/x86_64\/meuapp-*.rpm   # verifica<\/code><\/pre>\n<h2>Reposit\u00f3rio dnf pr\u00f3prio<\/h2>\n<pre><code class=\"language-bash\">sudo dnf install createrepo_c\nmkdir -p \/srv\/repo\/el9\/x86_64\ncp ~\/rpmbuild\/RPMS\/x86_64\/*.rpm \/srv\/repo\/el9\/x86_64\/\ncreaterepo_c \/srv\/repo\/el9\/x86_64\/<\/code><\/pre>\n<p>Sirva com nginx e, no cliente, um <code>.repo<\/code> resolve:<\/p>\n<pre><code class=\"language-bash\">cat | sudo tee \/etc\/yum.repos.d\/meurepo.repo << 'EOF'\n[meurepo]\nname=Meu Repositorio\nbaseurl=https:\/\/repo.example.com\/el9\/$basearch\/\nenabled=1\ngpgcheck=1\ngpgkey=https:\/\/repo.example.com\/chave.gpg\nEOF\nsudo dnf install meuapp<\/code><\/pre>\n<p>Publicou pacote novo? Rode <code>createrepo_c --update<\/code> e os clientes recebem via <code>dnf upgrade<\/code>.<\/p>\n<h2>Atalho: fpm<\/h2>\n<p>O mesmo <a href=\"https:\/\/fpm.readthedocs.io\/\" rel=\"noopener\" target=\"_blank\">fpm<\/a> do post anterior tamb\u00e9m cospe RPM \u2014 para ferramenta interna, resolve em um comando:<\/p>\n<pre><code class=\"language-bash\">fpm -s dir -t rpm -n meuapp -v 1.0.0 --description \"Meu utilit\u00e1rio\" \n  --depends ca-certificates meuapp=\/usr\/bin\/meuapp<\/code><\/pre>\n<h2>Resumo pr\u00e1tico<\/h2>\n<ul>\n<li><strong>Ferramenta interna?<\/strong> <code>fpm<\/code> ou um spec m\u00ednimo \u2014 r\u00e1pido e suficiente<\/li>\n<li><strong>Publicando de verdade?<\/strong> spec completo + <code>rpmlint<\/code> limpo + build no <code>mock<\/code> + RPM assinado + reposit\u00f3rio com <code>createrepo_c<\/code><\/li>\n<li>Config com <code>%config(noreplace)<\/code>, systemd com as macros <code>%systemd_*<\/code>, caminhos sempre via macros<\/li>\n<\/ul>\n<p>Com os dois posts da dupla \u2014 <a href=\"\/2026\/09\/criando-pacotes-deb\/\">.deb<\/a> e .rpm \u2014 voc\u00ea cobre praticamente todo servidor Linux em produ\u00e7\u00e3o. D\u00favidas ou um caso cabeludo de empacotamento? Me chame na <a href=\"\/contato\/\">p\u00e1gina de contato<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the first part of this pair, we built .deb packages for Debian and Ubuntu. Now it is the Red Hat family\u2019s turn \u2014 RHEL, Fedora, Rocky, AlmaLinux, and, by extension, openSUSE: we will create .rpm packages from scratch, from the spec file to our own dnf repository, covering rpmbuild, rpmlint, mock, and GPG signing. How RPM works Unlike \u2026 <a title=\"Creating .rpm packages for the Red Hat family\" class=\"read-more\" href=\"https:\/\/www.linuxpro.com.br\/en\/2026\/09\/criando-pacotes-rpm\/\" aria-label=\"Read more about Criando pacotes .rpm para a fam\u00edlia Red Hat\">Read more<\/a><\/p>","protected":false},"author":0,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[33,2,83],"tags":[337,340,336,339,338,341],"class_list":["post-692","post","type-post","status-publish","format-standard","hentry","category-howtos","category-linux","category-opensuse","tag-empacotamento","tag-fedora","tag-pacotes","tag-redhat","tag-rpm","tag-rpmbuild"],"_links":{"self":[{"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/posts\/692","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/comments?post=692"}],"version-history":[{"count":4,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/posts\/692\/revisions"}],"predecessor-version":[{"id":1208,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/posts\/692\/revisions\/1208"}],"wp:attachment":[{"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/media?parent=692"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/categories?post=692"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/tags?post=692"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}