O que é o Podman

A linha de comando do Podman é muito parecida com a do Docker; a maior diferença é que ele não tem um daemon residente. O Docker depende de um dockerd que roda constantemente como root para gerenciar os contêineres; o Podman não tem esse papel. Isso traz duas consequências diretas: os contêineres podem ser criados e executados por usuários comuns (modo rootless), e também podem ser entregues ao systemd para serem gerenciados como serviços.

O Podman também tem alguns subcomandos que o Docker não tem, como pod (grupo de contêineres), kube, quadlet e auto-update; os dois últimos serão usados adiante.

Instalação no Ubuntu 26.04

sudo apt install podman

Em um contêiner Ubuntu 26.04.1 LTS (arm64), instalei o podman 5.7.0. O mesmo repositório também tem podman-compose (1.5.0), podman-docker, buildah, skopeo, crun e netavark. Executando podman info como root, ele mostra Rootless como false, o driver de armazenamento overlay, o runtime crun e o backend de rede netavark.

Verificando:

podman --version
podman run --rm docker.io/library/alpine:3.20 echo hello-from-podman

Se a saída for hello-from-podman, é possível executar contêineres. Escrevi o nome da imagem completo, docker.io/library/alpine:3.20, porque incluir o domínio do repositório é o que menos deixa margem a ambiguidade. Uma observação sobre meu ambiente de teste: testei de forma aninhada dentro de outro contêiner Docker; por causa das restrições de cgroup, precisei de um parâmetro extra para funcionar, o que você não vai precisar em um host real.

Se você sabe usar docker, sabe usar podman

Os subcomandos listados em podman --help cobrem praticamente os que o Docker usa no dia a dia: build, run, create, start, stop, restart, rm, ps, images, rmi, pull, push, logs, exec, inspect, cp, port, stats, top, volume, network, secret, entre outros. O podman run também suporta -p/--publish, -v/--volume, --userns, e o --restart pode assumir os valores always, no, never, on-failure, unless-stopped.

Um ponto de atenção: podman ps só mostra os contêineres criados pelo usuário atual. Contêineres criados por um usuário comum não são visíveis para outros usuários, nem para o Podman executado como root. Diante da pergunta "para onde foi o contêiner", confirme primeiro com qual usuário o comando foi executado.

Duas formas de transição

Instalar podman-docker. Depois de instalar esse pacote, /usr/bin/docker é um script shell bem curto: ele primeiro verifica se /etc/containers/nodocker (ou ~/.config/containers/nodocker) existe; se não existir, escreve na saída de erro a linha Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg. e então faz exec /usr/bin/podman "$@". Por isso, docker --version na verdade exibe podman version 5.7.0. Se achar o aviso irritante, basta criar esse arquivo:

sudo apt install podman-docker
sudo touch /etc/containers/nodocker

Ele apenas repassa o comando ao Podman. Cada subcomando e parâmetro docker usado nos seus scripts precisa ser testado por você para ver se tem correspondência no Podman; programas que dependem da API do daemon do Docker (como docker.sock) não foram verificados por mim.

Usar podman compose. O podman compose não implementa compose por conta própria; a ajuda diz que ele executa por meio de um provedor externo, como docker-compose ou podman-compose. Ao executar podman compose version no contêiner, ele primeiro imprimiu o caminho do provedor externo que estava chamando e depois exibiu o número de versão do próprio provedor (Docker Compose version 2.40.3+ds1-0ubuntu1). Portanto, antes de usá-lo, a máquina precisa ter um provedor de compose instalado.

Preparação para o modo rootless: subuid e subgid

Ao executar o Podman como usuário comum, ele cria automaticamente um namespace de usuário para esse usuário, e os intervalos disponíveis estão escritos em /etc/subuid e /etc/subgid. A página de manual exige: o usuário precisa ter múltiplos intervalos de UID/GID nesses dois arquivos. O método de adição indicado pelo manual:

sudo usermod --add-subuids 10000-75535 nome-do-usuário
sudo usermod --add-subgids 10000-75535 nome-do-usuário

Os locais de dados do rootless também são diferentes dos do root: a configuração é lida de $XDG_CONFIG_HOME, ou de ~/.config/containers quando não definida; as imagens ficam em $XDG_DATA_HOME, ou em ~/.local/share/containers/storage quando não definido; os dados temporários vão em ${XDG_RUNTIME_DIR}/containers. Para diagnosticar problemas de rootless, comece por esses diretórios.

Usando o Quadlet para que contêineres iniciem com o sistema

O podman generate systemd já aparece marcado como [DEPRECATED] na ajuda do 5.7.0. A abordagem atual é o Quadlet: ele é o gerador de systemd do Podman, lê arquivos .container, .volume, .network, .build, .pod, .kube, .artifact e gera um .service de mesmo nome para cada arquivo. Esses arquivos são lidos no boot e ao executar systemctl daemon-reload, tanto no systemd em nível de sistema quanto em nível de usuário.

Onde colocar. Os caminhos de busca para root, em ordem de prioridade, são: /run/containers/systemd/ (temporário, para testes), /etc/containers/systemd/ (definidos pelo administrador) e /usr/share/containers/systemd/ (definidos pela distribuição). Para usuários comuns, são $XDG_RUNTIME_DIR/containers/systemd/, ~/.config/containers/systemd/ e os diretórios sob /etc/containers/systemd/users/.

Como escrever. Abaixo está um exemplo que realmente usei, salvo como /etc/containers/systemd/web.container:

[Unit]
Description=Demo web server

[Container]
Image=docker.io/library/nginx:1.27-alpine
PublishPort=8080:80
AutoUpdate=registry

[Service]
Restart=always

[Install]
WantedBy=default.target

A seção [Container] usa a sintaxe própria do Quadlet: Image= especifica a imagem e PublishPort= corresponde ao --publish do podman run. As seções [Service], [Install] e afins são passadas ao systemd sem alterações, então Restart=always funciona como em qualquer serviço systemd escrito normalmente.

O que foi gerado. Não é preciso escrever o arquivo de serviço à mão; para ver o resultado gerado, use o dry-run, que apenas imprime, sem instalar:

/usr/lib/systemd/system-generators/podman-system-generator --dryrun

Para o arquivo acima, o web.service que ele gera tem como ExecStart:

/usr/bin/podman run --name systemd-%N --replace --rm --cgroups=split --sdnotify=conmon -d --label io.containers.autoupdate=registry --publish 8080:80 docker.io/library/nginx:1.27-alpine

Ele também adiciona Type=notify, Delegate=yes, KillMode=mixed, o ExecStop é /usr/bin/podman rm -v -f -i systemd-%N, e o [Unit] ganha After=network-online.target. Daí dá para tirar três conclusões: o nome do contêiner é fixado como systemd-<nome-da-unidade>; o contêiner leva --rm, então assim que o serviço para o contêiner é removido, sem deixar uma pilha de contêineres finalizados; e o AutoUpdate=registry se transforma na etiqueta io.containers.autoupdate=registry, que é o interruptor da atualização automática.

Esse resultado gerado foi o que validei com o dry-run. Meu ambiente de teste não tinha systemd em execução, portanto nunca executei de fato o systemctl para iniciá-lo. O procedimento padrão é sudo systemctl daemon-reload seguido de sudo systemctl start web.service; se o contêiner não subir, veja os logs com journalctl -u web.service. Para ver os contêineres criados por unidades em nível de sistema, execute sudo podman ps como root — e não esqueça o que foi dito antes: contêineres de usuários diferentes não são visíveis entre si.

Há ainda o grupo de subcomandos podman quadlet — install, list, print, rm — usado para gerenciar esses arquivos em si.

Atualização automática

A ajuda do podman auto-update diz que ele trata de contêineres executados por unidades systemd, com a política de atualização especificada pela etiqueta io.containers.autoupdate. É exatamente essa etiqueta que o AutoUpdate=registry acima gera. Disparando manualmente uma vez:

sudo podman auto-update

O sudo é necessário porque os contêineres criados por unidades em nível de sistema pertencem ao root. O significado completo da política registry, e como fazer com que ela execute periodicamente, não foram aprofundados por mim; consulte man podman-auto-update.

Algumas dúvidas comuns

  • O comando docker exibe uma linha extra de aviso a cada execução: crie /etc/containers/nodocker (para todos os usuários) ou ~/.config/containers/nodocker (para o usuário atual).
  • podman compose não encontra provedor: instale primeiro podman-compose ou docker-compose.
  • Contêiner rootless não inicia: verifique se o usuário atual tem múltiplos intervalos em /etc/subuid e /etc/subgid.
  • Alteração no arquivo .container não surtiu efeito: os arquivos Quadlet só são lidos no boot e no daemon-reload; depois de alterar, rode primeiro sudo systemctl daemon-reload e depois reinicie o serviço correspondente.