SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor · Atualizado 2026-09-04

Como instalar Docker no Rocky Linux e AlmaLinux

Instale o Docker Engine com dnf e Compose no Rocky Linux ou AlmaLinux. Corrija o Podman controlando o comando docker e os bloqueios do SELinux em bind mounts.

Instalar Docker no Rocky Linux e no AlmaLinux

Para instalar Docker no Rocky Linux ou no AlmaLinux, adicione o repositório dnf do Docker, instale o engine com o plugin do 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. Tudo o que se segue aplica-se às duas distribuições. Se ainda estiver a escolher entre elas, os fatores decisivos são o compromisso de compatibilidade assumido por cada projeto e o suporte ao seu CPU mais antigo.

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 use o script de conveniência do Docker disponível em get.docker.com. A documentação do 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 atualizar o sistema. Adicionar o repositório manualmente faz com que dnf upgrade trate o Docker como qualquer outro pacote do sistema. Isso também inclui o engine no âmbito do dnf-automatic, se o tiver configurado para aplicar atualizações de segurança periodicamente, por isso decida desde cedo se pretende que o Docker seja atualizado sem intervenção ou fique retido até uma janela de manutenção. Em qualquer dos casos, uma atualização substitui o binário do pacote enquanto o processo dockerd antigo continua em execução, e needs-restarting é o comando que indica quais serviços ainda estão a executar o código que acabou de substituir.

O podman já está a responder 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 o podman, pelo que 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 não existe, apresenta uma linha antes de executar qualquer ação:

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 dele. Consulte a base de dados de pacotes para saber qual é o 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. Leia o script antes de confiar nele.

O podman executa as mesmas imagens OCI e é uma opção razoável. Se pretende utilizá-lo, pode parar aqui. Ambos são motores de contentores Linux. Se a decisão da plataforma ainda estiver em aberto, vale a pena saber que as jails do FreeBSD isolam um userland completo em vez de executarem imagens em camadas obtidas de um registry. Se pretende utilizar 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, remover podman pode remover cockpit-podman ou outra ferramenta que dependa dele.

Em princípio, é possível manter o podman juntamente com o Docker: 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 acima.

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

O Docker publica RPMs para Enterprise Linux em download.docker.com. O ficheiro do repositório aponta para a árvore do CentOS, que é a árvore contra a qual Rocky Linux e AlmaLinux resolvem os pacotes. Apontar uma máquina Rocky para um repositório CentOS parece um erro até saber como ambas as distribuições surgiram da linhagem do CentOS depois de a Red Hat transformar o CentOS em Stream em 2020. 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, por isso o segundo comando falha em versões mais recentes. Verifique qual versão tem e escolha a forma correspondente:

dnf --version

Se mostrar 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 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 executar algo incorreto silenciosamente, por isso não passará despercebida.

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 de release. Rocky Linux e AlmaLinux definem-na como o número da versão principal. Assim, 9 em EL 9 e 10 em EL 10. É por isso que um repositório CentOS é resolvido corretamente numa máquina 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 release definir $releasever como uma versão específica, como 9.6, o dnf reportará 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 pontos.

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 introduz. 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 precisa de ser atualizado para docker compose com um espaço.

A primeira instalação para 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 aparece na lista, porque é nesse repositório 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 após a instalação. Por isso, este passo aparece na página do Docker para CentOS e não na página para Ubuntu, onde o pacote deb inicia o serviço automaticamente. Se ignorar enable, o Docker continuará em execução até ao próximo reboot. Depois ficará parado e fará parar todos os contentores.

systemctl status deve apresentar Active: active (running). O contentor hello-world deve imprimir This message shows that your installation appears to be working correctly. e terminar. Se, em vez disso, imprimir um erro de permissão 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 saudável é 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, e as políticas de reinício determinam se os serviços Compose voltam a arrancar no boot.

Por que um bind mount retorna permission denied?

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 executam sob o tipo SELinux container_t, e esse tipo só pode ler e escrever ficheiros rotulados como 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 acabado de ver no diretório. A divergência 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 usem o mesmo diretório. :Z em maiúsculas altera o rótulo para private e unshared, associando-o a um único contentor. Um segundo contentor que leia o mesmo caminho terá então 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. Fazer bind mount de um diretório do sistema, como /home ou /usr, com :Z "torna a máquina host inoperável e pode 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 é colocado na 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, portanto use -v quando precisar de 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. Reponha-o imediatamente com sudo setenforce 1. No Enterprise Linux, permission denied num bind mount pode ter duas causas distintas que parecem idênticas dentro do contentor. Uma é o rótulo SELinux. A outra é a propriedade numérica normal do utilizador e do grupo, que é o motivo da existência das variáveis PUID e PGID. ls -lnZ mostra o modo, o proprietário numérico e o rótulo numa única linha, permitindo determinar qual dos dois problemas está a resolver.

Por que uma porta publicada está acessível quando o firewalld parece fechado?

O firewalld é a firewall predefinida no Rocky Linux e no AlmaLinux. Verifique se está em execução com sudo systemctl is-active firewalld. Se ainda não configurou o firewalld neste servidor, comece por abrir o SSH e uma porta Web com o firewalld, porque o problema abaixo só pode ser analisado depois de existir um conjunto funcional de regras de zona para comparação. Agora publique uma porta e veja 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 a firewall não mostra nada.

A causa é o percurso seguido pelo pacote. As regras de zona do firewalld filtram o tráfego destinado ao próprio servidor. Uma porta publicada não é destinada ao servidor: o Docker instala uma regra de NAT de destino (tradução de endereços de rede) que altera o destino para o endereço do contentor antes de o pacote chegar ao percurso de entrada do servidor. Assim, o kernel encaminha o pacote em vez de o entregar localmente. O Docker coloca depois 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 requer uma regra de firewall. Associe o lado do servidor 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 servidor 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 permitir o acesso a um serviço a partir de alguns endereços, mas não de outros, o Docker reserva uma chain para esse efeito. DOCKER-USER é processada antes das próprias regras de aceitação do Docker. Assim, 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 atuais de EL usam nomes como enp1s0 ou ens3. No Rocky Linux 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 desta forma desaparecem depois de um reboot, a menos que as guarde. Quando estiverem validadas, coloque-as numa unidade systemd.

O Docker Engine 28.0, lançado em 2025, corrigiu uma falha semelhante: o acesso direto encaminhado a portas de contentores que nunca foram publicadas passou a ser bloqueado na chain DOCKER. Essa alteração não afeta as portas publicadas, portanto tudo o que foi descrito acima continua a aplicar-se às versões atuais. Vale a pena criar um hábito 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 outra ferramenta, como explicado em por que as portas publicadas do Docker ignoram as regras do ufw. Em ambos os casos, a causa é o percurso de NAT. Apenas a firewall colocada à frente dele é diferente.

Adicione um utilizador sem privilégios 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 sua shell atual já tem a lista de grupos definida, por isso a alteração não se aplica até iniciar uma nova sessão. newgrp docker inicia uma shell com o grupo associado, para que possa testar imediatamente. As novas sessões SSH obtêm essa associação automaticamente.

Tenha claro o que esse grupo permite. A associação concede acesso de escrita a /var/run/docker.sock, e qualquer processo que consiga comunicar com esse socket pode pedir ao daemon para iniciar um contentor que monte o sistema de ficheiros do anfitrião. 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 concederia sudo. Se estiver a configurar contas num servidor novo, tome esta decisão em conjunto com o restante configuração de utilizadores com privilégios mínimos num 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 opção que possa adicionar mais tarde.

Próximos passos

Agora tem o engine, o plugin do Compose, um serviço que continua disponível depois de um reboot e os três comportamentos específicos do EL documentados acima. O passo seguinte é um compose.yaml por serviço, e a anatomia de um ficheiro Compose explica o formato do ficheiro e os comandos que o controlam. Se este for o seu primeiro host de contentores, executar o Docker numa VPS aborda as questões de dimensionamento, armazenamento e gestão de 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 os metadados indica que a variável foi expandida para uma versão intermédia. Edite /etc/yum.repos.d/docker-ce.repo para usar apenas o número da versão principal.

Posso instalar Docker e podman no mesmo servidor?

A documentação do Docker lista podman e runc como pacotes incompatíveis e indica que deve removê-los 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 podman. Execute rpm -qf "$(command -v docker)" para ver qual pacote é proprietário desse caminho. Se o resultado 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.

Por que o meu contentor recebe "permission denied" num bind mount?

O SELinux está, por predefinição, em modo enforcing no Rocky Linux e no AlmaLinux. Os contentores são executados com o tipo container_t e só podem aceder a ficheiros identificados com container_file_t. Por isso, um diretório que criou tem o rótulo errado 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 apresenta avc: denied com os dois contextos diferentes. 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 danificará o anfitrião.

Preciso de abrir uma porta no firewalld para publicar a 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 percurso 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 apresenta qualquer resultado. Publique para um endereço específico com -p 127.0.0.1:8080:80 quando apenas o anfitrião deve 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 acesso equivalente a 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 a 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 método de instalação separado, não uma definição.