SSD Nodes Learn 🎉 VPS desde $5.50/mês
Guias Matt ConnorPor Matt Connor · Atualizado 2026-08-13

Como instalar Docker no Rocky Linux e AlmaLinux

Instale Docker Engine com dnf no Rocky Linux e AlmaLinux. Resolva o conflito do Podman com o comando docker e erros do SELinux em bind mounts.

Instalar Docker no Rocky Linux e no AlmaLinux

Para instalar o Docker no Rocky Linux ou no AlmaLinux, adicione o repositório dnf do próprio Docker, instale o engine com o plugin Compose e ative o serviço. Essa parte requer quatro comandos e é idêntica nas duas distribuições, porque ambas são reconstruções do Red Hat Enterprise Linux (RHEL) e partilham a mesma organização de pacotes. O CentOS Stream funciona da mesma forma.

A instalação é curta, por isso a maior parte deste guia aborda as diferenças do Enterprise Linux (EL) em relação ao Ubuntu. O Podman pode já controlar o comando docker na sua imagem. O SELinux bloqueia ficheiros montados com bind mount até que tenham o rótulo correto. O Firewalld não filtra as portas publicadas pelo Docker, por isso uma porta de contentor pode estar aberta à Internet enquanto firewall-cmd indica que não há nenhuma porta aberta.

Não utilize o script de conveniência do Docker disponível em get.docker.com. A documentação do próprio Docker afirma que ele não é recomendado para produção. O script reescreve a configuração dos repositórios sem pedir confirmação e não pode ser executado novamente com segurança para fazer uma atualização. Adicionar o repositório manualmente faz com que dnf upgrade trate o Docker como qualquer outro pacote do sistema.

O podman já está respondendo ao comando docker?

Rocky Linux e AlmaLinux incluem o podman nos seus repositórios predefinidos, e muitas imagens de VPS instalam-no automaticamente. Algumas imagens vão mais longe e instalam podman-docker, que coloca um script de shell em /usr/bin/docker para chamar o podman. Todos os comandos docker que introduzir passam então a executar podman, e um guia escrito para Docker começa a produzir resultados inesperados.

O primeiro indício é um banner. O script /usr/bin/docker verifica se o ficheiro /etc/containers/nodocker existe. Quando o ficheiro está ausente, apresenta uma linha antes de executar qualquer outra coisa:

Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.

Alguém pode ter criado esse ficheiro para ocultar o banner, por isso não dependa apenas desse sinal. Consulte a base de dados de pacotes para saber qual pacote é proprietário do binário:

command -v docker
rpm -qf "$(command -v docker)"

Uma resposta que comece por podman-docker significa que o podman está a responder. Uma resposta que comece por docker-ce-cli significa que é o Docker real. Se rpm -qf indicar que nenhum pacote é proprietário do ficheiro, alguém instalou-o manualmente. Nesse caso, leia o script antes de confiar nele.

O Podman executa as mesmas imagens OCI e é uma opção adequada. Se quiser utilizá-lo, pare aqui. Se quiser o Docker Engine, remova primeiro os pacotes em conflito. Esta é a lista documentada pelo Docker para RHEL:

sudo dnf remove docker docker-client docker-client-latest docker-common \
  docker-latest docker-latest-logrotate docker-logrotate docker-engine podman runc

Leia o que o dnf planeia remover antes de confirmar. Numa imagem de VPS nova, a lista é curta. Num servidor que já tenha sido utilizado, a remoção de podman pode remover cockpit-podman ou outra ferramenta que dependa dele.

É possível manter o podman juntamente com o Docker, em princípio: remova apenas podman-docker, para libertar o nome docker, e runc, que o pacote containerd.io substitui. A documentação do Docker trata o podman como um pacote em conflito, por isso esta configuração não é suportada pelo Docker. Se a instalação continuar a indicar um conflito, utilize a lista completa de remoção apresentada acima.

Adicione o repositório do Docker com dnf config-manager

O Docker publica pacotes RPM para Enterprise Linux em download.docker.com. O ficheiro do repositório aponta para a árvore do CentOS, que é aquela contra a qual Rocky Linux e AlmaLinux resolvem os pacotes. Verificado em agosto de 2026, o Docker documenta este repositório para CentOS Stream 9 e CentOS Stream 10.

sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

A versão 5 do dnf removeu o argumento --add-repo, pelo que esse segundo comando falha nas versões mais recentes. Verifique qual versão está instalada e escolha a forma correspondente:

dnf --version

Se for apresentada uma versão 5.x, use a forma com subcomando:

sudo dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/centos/docker-ce.repo

Ambas as formas escrevem o mesmo ficheiro em /etc/yum.repos.d/docker-ce.repo. A forma incorreta falha com um erro de argumento desconhecido, em vez de produzir um resultado incorreto de forma silenciosa, pelo que o problema será evidente.

Esse ficheiro do repositório define baseurl como um caminho que contém $releasever, e o dnf expande essa variável a partir do pacote da versão instalada. Rocky Linux e AlmaLinux definem-na como o número da versão principal. Por isso, 9 em EL 9 e 10 em EL 10. É essa a razão pela qual um repositório do CentOS é resolvido corretamente num sistema Rocky. Confirme a expansão antes de instalar:

sudo dnf repoinfo docker-ce-stable

Leia a linha Repo-baseurl. Ela deve terminar em /9/x86_64/stable ou /10/x86_64/stable. Se a sua versão definir $releasever como uma versão pontual, como 9.6, o dnf comunica Status code: 404 para esse URL ao obter os metadados. Corrija o problema editando /etc/yum.repos.d/docker-ce.repo e substituindo $releasever pelo número da versão principal sem elementos adicionais.

Instale o engine e o plugin Compose

sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

São cinco pacotes, e cada um tem uma função. docker-ce é o daemon, dockerd. docker-ce-cli é o comando docker que escreve. containerd.io é o runtime de contentores controlado pelo daemon. docker-buildx-plugin cria imagens. docker-compose-plugin disponibiliza docker compose como subcomando.

Estes pacotes não instalam um binário docker-compose com hífen. Esse era o Compose v1, que chegou ao fim de vida em julho de 2023. Tudo o que chamar docker-compose com um hífen deve ser atualizado para docker compose com um espaço.

A primeira instalação é interrompida para importar a chave de assinatura do Docker e mostra a respetiva impressão digital. A chave vem de gpgkey=https://download.docker.com/linux/centos/gpg no ficheiro do repositório que acabou de adicionar. Compare a impressão digital apresentada pelo dnf com esse URL antes de a aceitar.

Há uma falha suficientemente comum para ser mencionada. Se o dnf indicar que containerd.io requer container-selinux e que nenhum pacote o fornece, o repositório AppStream está desativado. Execute dnf repolist e confirme que appstream está listado, porque é aí que container-selinux é disponibilizado no EL 9 e no EL 10.

Inicie o Docker e confirme que está em execução

sudo systemctl enable --now docker
sudo systemctl status docker --no-pager
sudo docker run --rm hello-world

Os pacotes RPM do Docker deixam o daemon parado e desativado depois da instalação. Por isso, este passo aparece na página do Docker para CentOS, mas não na página para Ubuntu, onde o pacote deb inicia o serviço automaticamente. Ignore enable e o Docker continuará em execução até ao próximo reboot. Depois ficará parado e interromperá todos os containers.

systemctl status deve mostrar Active: active (running). O container hello-world deve imprimir This message shows that your installation appears to be working correctly. e terminar. Se imprimir um erro de permissões em /var/run/docker.sock, faltou indicar sudo. A secção sobre o grupo docker abaixo corrige esse problema.

Verifique o plugin Compose separadamente, porque é um pacote diferente e pode estar em falta mesmo quando o engine está a funcionar:

docker compose version

Uma resposta correta é semelhante a Docker Compose version v2.x.x. Recuperar os seus serviços depois de um reboot é uma questão diferente de ativar o daemon. as políticas de reinício determinam se os serviços do Compose voltam a arrancar no boot.

Por que uma montagem bind resulta em permissão negada?

Rocky Linux e AlmaLinux executam o SELinux (Security-Enhanced Linux) no modo enforcing por padrão. Confirme com getenforce, que imprime Enforcing.

Os contentores Docker são executados com o tipo SELinux container_t, e esse tipo só pode ler e escrever ficheiros identificados com container_file_t. Um diretório criado no host recebe o rótulo definido pelo caminho pai, que não é container_file_t. O contentor tem o acesso negado, mesmo que o proprietário, o grupo e o modo pareçam corretos no host. Reproduza o problema com três comandos:

sudo mkdir -p /srv/site
echo hello | sudo tee /srv/site/index.html
sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro nginx:alpine cat /usr/share/nginx/html/index.html

O contentor imprime:

cat: can't open '/usr/share/nginx/html/index.html': Permission denied

Dois comandos mostram a causa. ls -ldZ /srv/site imprime o rótulo, que, para um caminho sob /srv, é system_u:object_r:var_t:s0, e não container_file_t. Em seguida, sudo ausearch -m avc -ts recent imprime o registo de auditoria do kernel, que contém avc: denied { read }, um campo scontext= que identifica container_t, e um campo tcontext= que identifica o rótulo que acabou de ver no diretório. A diferença entre esses dois campos explica todo o problema.

A correção é adicionar um sufixo ao argumento do volume. O Docker altera o rótulo do caminho:

sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro,z nginx:alpine cat /usr/share/nginx/html/index.html

:z, em minúsculas, altera o rótulo do conteúdo para shared, permitindo que vários contentores utilizem o mesmo diretório. :Z, em maiúsculas, altera o rótulo para private e unshared, associando-o a um único contentor. Nesse caso, um segundo contentor que leia o mesmo caminho terá o acesso negado. Use :z para qualquer diretório que também seja utilizado por um sidecar ou por um contentor de backup. Use :Z para um diretório de base de dados pertencente a um único contentor.

A documentação do Docker inclui um aviso que vale a pena repetir, porque a alteração do rótulo é recursiva. Montar um diretório do sistema, como /home ou /usr, com :Z "torna a sua máquina host inoperável e poderá ser necessário alterar manualmente os rótulos dos ficheiros da máquina host". Aponte estes sufixos para diretórios criados para o contentor, nunca para um caminho do sistema.

No Compose, o sufixo é acrescentado à mesma string:

services:
  web:
    image: nginx:alpine
    volumes:
      - /srv/site:/usr/share/nginx/html:ro,z

É fácil atingir dois limites. A flag --mount não pode definir um rótulo SELinux. Por isso, use -v quando precisar de definir um. Os volumes nomeados não precisam de sufixo, porque o Docker altera automaticamente os rótulos dos diretórios que cria em /var/lib/docker/volumes.

Não desative o SELinux. Use sudo setenforce 0 apenas como teste de um minuto. Se o contentor funcionar nesse caso, o problema é um rótulo e :z é a solução. Reative-o imediatamente com sudo setenforce 1. No Enterprise Linux, permission denied numa montagem bind pode ter duas causas distintas que parecem idênticas dentro do contentor. Uma é o rótulo SELinux. A outra é a propriedade normal, baseada em IDs numéricos, do utilizador e do grupo. É para resolver isto que existem as variáveis PUID e PGID. ls -lnZ mostra o modo, o proprietário numérico e o rótulo numa única linha, para que possa identificar qual dos dois problemas está a resolver.

Por que uma porta publicada pode ser alcançada quando o firewalld parece fechado?

O firewalld é o firewall predefinido no Rocky Linux e no AlmaLinux. Verifique se está em execução com sudo systemctl is-active firewalld. Agora publique uma porta e consulte o que o firewalld considera aberto:

sudo docker run -d --name web -p 8080:80 nginx:alpine
sudo firewall-cmd --list-ports

firewall-cmd imprime uma linha vazia. A partir de outra máquina, curl -I http://YOUR_SERVER_IP:8080/ devolve HTTP/1.1 200 OK. A porta está aberta para a Internet, mas o firewall não mostra essa abertura.

A causa é o caminho seguido pelo pacote. As regras de zona do firewalld filtram o tráfego destinado ao próprio host. Uma porta publicada não é destinada ao host: o Docker instala uma regra de destination NAT (tradução de endereços de rede) que reescreve o destino para o endereço do contentor antes de o pacote alcançar o caminho de entrada do host. Por isso, o kernel encaminha o pacote em vez de o entregar localmente. Em seguida, o Docker coloca as interfaces bridge numa zona do firewalld chamada docker, cujo destino é ACCEPT, e adiciona uma política de encaminhamento chamada docker-forwarding que permite o encaminhamento de qualquer zona para a zona docker. As regras da sua zona nunca veem o pacote.

A correção mais simples não precisa de uma regra de firewall. Vincule o lado do host da publicação à interface de loopback e coloque um reverse proxy à frente:

sudo docker rm -f web
sudo docker run -d --name web -p 127.0.0.1:8080:80 nginx:alpine
curl -I http://127.0.0.1:8080/

O comando local curl devolve HTTP/1.1 200 OK, e o mesmo pedido a partir de outra máquina deixa de estabelecer ligação. Qualquer publicação sem um endereço de host no argumento -p é feita em todas as interfaces. Por isso, trate um -p 8080:80 sem endereço como uma decisão de expor publicamente esse serviço.

Quando precisar de que um serviço seja alcançável a partir de alguns endereços, mas não de outros, o Docker reserva uma chain para esse fim. DOCKER-USER é processada antes das próprias regras de aceitação do Docker. Por isso, uma regra colocada nessa chain permanece depois de o Docker reiniciar e reescrever as suas chains:

sudo iptables -I DOCKER-USER -i enp1s0 ! -s 203.0.113.10 -j DROP
sudo iptables -S DOCKER-USER

Obtenha o nome da interface a partir de ip route show default em vez de assumir eth0, porque as imagens EL atuais usam nomes como enp1s0 ou ens3. No Rocky e no AlmaLinux, o comando iptables é uma camada de compatibilidade sobre nftables, e as chains do Docker ficam visíveis através dele. As regras adicionadas dessa forma desaparecem depois de um reboot, a menos que as guarde. Por isso, escreva-as numa unidade systemd quando estiver satisfeito com a configuração.

O Docker Engine 28.0, lançado em 2025, corrigiu uma falha relacionada: o acesso roteado diretamente a portas de contentores que nunca foram publicadas passou a ser bloqueado na chain DOCKER. Essa alteração não afeta as portas publicadas. Tudo o que foi descrito acima continua a aplicar-se às versões atuais. Vale a pena adotar um procedimento operacional: depois de qualquer sudo firewall-cmd --reload, teste novamente uma porta publicada. Se deixar de responder, sudo systemctl restart docker reinstala as regras do Docker.

Os administradores de Ubuntu encontram o mesmo problema através de uma ferramenta diferente. Consulte por que as portas Docker publicadas ignoram as regras do ufw. O caminho NAT é a causa nos dois casos. Apenas o firewall colocado à frente dele é diferente.

Adicionar um utilizador não root ao grupo docker

Escrever sudo antes de cada comando docker torna-se cansativo, e o grupo docker elimina essa necessidade:

sudo usermod -aG docker $USER
newgrp docker
docker run --rm hello-world

usermod -aG edita /etc/group, mas a shell atual já tem a sua lista de grupos. Por isso, a alteração só se aplica quando obtiver uma nova shell. newgrp docker inicia uma shell com o grupo associado, para poder testar imediatamente. As novas sessões SSH assumem essa alteração automaticamente.

Tenha claro o que esse grupo permite. A pertença ao grupo dá acesso de escrita a /var/run/docker.sock. Qualquer processo que consiga comunicar com esse socket pode pedir ao daemon para iniciar um contentor que monte o sistema de ficheiros do host. Um comando mostra o que isso significa:

docker run --rm -v /:/host alpine wc -l /host/etc/shadow

Esse comando lê um ficheiro que apenas root pode ler, a partir de uma conta sem permissões sudo. A documentação de pós-instalação do Docker afirma o mesmo: o grupo docker concede privilégios equivalentes aos de root. Adicione uma conta a esse grupo apenas se também lhe concedesse sudo. Se estiver a configurar contas num servidor novo, tome esta decisão juntamente com o resto da sua configuração de utilizadores com privilégio mínimo numa VPS, e não posteriormente.

O Docker também disponibiliza um modo rootless, que executa o daemon como um utilizador sem privilégios. É um método de instalação separado e altera o comportamento dos controladores de armazenamento e das portas abaixo de 1024. Por isso, planeie-o como um projeto próprio, e não como uma flag a adicionar mais tarde.

O que fazer a seguir

Agora tem o engine, o plugin do Compose, um serviço que sobrevive a reboots e os três comportamentos específicos do EL documentados acima. O passo seguinte é um compose.yaml por serviço, e a estrutura de um ficheiro Compose aborda o formato do ficheiro e os comandos que o controlam. Se este for o seu primeiro host de contentores, executar Docker num VPS aborda as questões de dimensionamento, armazenamento e higiene das imagens que este guia não cobre.

FAQ

O repositório do Docker para CentOS funciona no Rocky Linux e no AlmaLinux?

Sim. Adicione https://download.docker.com/linux/centos/docker-ce.repo com dnf config-manager. O baseurl nesse ficheiro contém $releasever, e o Rocky Linux e o AlmaLinux expandem-no para o número da versão principal. Assim, um sistema EL 9 resolve para a árvore CentOS 9 e um sistema EL 10 para a árvore CentOS 10. Confirme a expansão com sudo dnf repoinfo docker-ce-stable e leia a linha Repo-baseurl. Um Status code: 404 quando o dnf obtém metadados significa que a variável foi expandida para uma versão de atualização. Editar /etc/yum.repos.d/docker-ce.repo para usar apenas o número da versão principal corrige o problema.

O Docker e o podman podem ser instalados no mesmo servidor?

A documentação do Docker lista podman e runc como pacotes incompatíveis e indica que ambos devem ser removidos antes de instalar o Docker Engine. O conflito concreto é o pacote podman-docker, que é proprietário de /usr/bin/docker e transforma todos os comandos docker em comandos do podman. Execute rpm -qf "$(command -v docker)" para ver que pacote é proprietário desse caminho. Se a saída começar por podman-docker, é o podman que está a responder. Manter os dois motores não é uma configuração suportada pelo Docker. Num servidor relevante, escolha um deles.

Porque é que o meu contentor recebe um erro de permissão num bind mount?

O SELinux está no modo enforcing por predefinição no Rocky Linux e no AlmaLinux. Os contentores executam como o tipo container_t e só podem aceder a ficheiros identificados com container_file_t. Por isso, um diretório criado por si tem o rótulo incorreto e o acesso é negado, independentemente do proprietário e das permissões. Confirme com ls -ldZ no caminho do anfitrião e com sudo ausearch -m avc -ts recent, que mostra avc: denied com os dois contextos incompatíveis. Adicione :z ao argumento do volume para conteúdo partilhado entre contentores, ou :Z para conteúdo privado de um só contentor. Nunca aponte :Z para /home ou /usr, porque a alteração recursiva dos rótulos irá quebrar o anfitrião.

É necessário abrir uma porta no firewalld para publicar uma porta de um contentor?

Não, e esse é o problema. A regra NAT do Docker reescreve o endereço de destino antes de o pacote chegar ao caminho de entrada do anfitrião. Por isso, as regras de zona do firewalld nunca o inspecionam. O Docker também coloca as suas bridges numa zona do firewalld chamada docker, com o destino ACCEPT. Um contentor iniciado com -p 8080:80 fica acessível a partir da Internet, enquanto sudo firewall-cmd --list-ports não mostra qualquer saída. Publique numa direção específica com -p 127.0.0.1:8080:80 quando apenas o anfitrião deve conseguir aceder ao serviço, ou insira regras de filtragem na cadeia DOCKER-USER, que o Docker processa antes das suas próprias regras de aceitação.

É seguro adicionar o meu utilizador ao grupo docker?

Isso concede root. Um membro do grupo docker pode escrever em /var/run/docker.sock, e docker run --rm -v /:/host alpine wc -l /host/etc/shadow pode então ler um ficheiro acessível apenas por root a partir de uma conta sem permissões de sudo. A documentação de pós-instalação do Docker declara a mesma equivalência. Adicione apenas contas às quais já confiaria sudo e continue a usar sudo docker para contas partilhadas ou de serviço. O modo rootless é a alternativa quando precisa de executar contentores com um utilizador sem privilégios. É um caminho de instalação separado, não uma opção de configuração.