2 pontos por GN⁺ 2025-03-25 | 1 comentários | Compartilhar no WhatsApp
  • Como o Podman não tem daemon, é preciso haver um responsável por iniciar os contêineres de forma confiável após o boot do servidor, e o Quadlet resolve isso no formato de arquivos de serviço do systemd
  • O método antigo com podman generate systemd exigia tratar separadamente a criação do contêiner e a geração do arquivo de serviço, além de trazer o custo de edição manual sempre que o arquivo gerado mudava
  • O Quadlet coloca, em um arquivo .container dentro de ~/.config/containers/systemd, as configurações [Container], [Service] e [Install], permitindo gerenciar opções do Podman e o comportamento do systemd em um só lugar
  • No Podman rootless, para iniciar automaticamente no boot, são necessários WantedBy=default.target e linger ativado; multi-user.target não é definido no modo de usuário
  • Com AutoUpdate=registry, dependências do systemd, agrupamento de arquivos por diretório e até a ferramenta de conversão podlet, o Quadlet se encaixa bem na operação do Podman rootless e sem daemon

Por que o Quadlet é necessário

  • Quadlet é uma forma de executar contêineres Podman como serviços do systemd
    • Permite executar contêineres em segundo plano
    • Permite iniciar contêineres automaticamente após reinicializações do servidor
  • A ideia de executar contêineres Podman sob o systemd não é nova
    • Antes, usava-se o comando podman generate systemd
    • Esse comando agora exibe um aviso de descontinuação recomendando migrar para o Quadlet
  • Como o Podman tem arquitetura daemonless, é necessário um responsável por iniciar os contêineres sem depender de um daemon

As limitações do método antigo com podman generate systemd

  • No método antigo, primeiro era preciso criar o contêiner com podman create
    • O contêiner de exemplo usa a imagem docker.io/library/postgres:16
    • Inclui -p 5432:5432, montagem de volume, variável de ambiente POSTGRES_PASSWORD e o rótulo io.containers.autoupdate=registry
  • Depois, executava-se podman generate systemd test-db -fn --new para gerar o arquivo container-test-db.service
  • O arquivo de serviço gerado era colocado em ~/.config/systemd/user e ativado/iniciado com o comando a seguir
    • systemctl --user enable --now container-test-db
  • Esse fluxo fazia repetir criação do contêiner, geração do arquivo de serviço, movimentação do arquivo quando necessário e ativação do serviço
  • Quando o comando de criação do contêiner ficava longo, era preciso gerenciar separadamente um script de shell para executá-lo novamente depois
  • Para customizar o arquivo de serviço do systemd gerado, era necessário fazer edições manuais toda vez

Estrutura do arquivo Quadlet

  • No modelo Quadlet, cria-se o diretório ~/.config/containers/systemd e coloca-se dentro dele um arquivo .container
  • O arquivo de exemplo test-db.container inclui os seguintes elementos
    • [Container]
      • Image=docker.io/library/postgres:16
      • AutoUpdate=registry
      • PublishPort=5432:5432
      • Volume=%h/volumes/test-db:/var/lib/postgresql/data:Z
      • Environment=POSTGRES_PASSWORD=CHANGE_ME
    • [Service]
      • Restart=always
    • [Install]
      • WantedBy=default.target
  • O arquivo .container é um arquivo de serviço normal do systemd, mas inclui uma seção especial [Container]
  • Muitas opções de [Container] correspondem a opções de linha de comando usadas em podman create
    • Image: imagem e tag a serem usadas
    • AutoUpdate=registry: corresponde a --label "io.containers.autoupdate=registry"
    • PublishPort: corresponde a -p
    • Volume: corresponde a -v
    • Environment: corresponde a -e
  • No diretório home do usuário, deve-se usar o especificador do systemd %h em vez de ~
  • Restart=always em [Service] faz o contêiner reiniciar sempre, a menos que seja parado manualmente
  • WantedBy=default.target em [Install] faz o contêiner iniciar automaticamente no boot

Configurações do systemd necessárias no Podman rootless

  • Em contêineres rootless, deve-se usar default.target em vez de multi-user.target
    • multi-user.target não é definido no modo de usuário do systemd
    • Isso pode ser verificado com systemctl --user status multi-user.target
    • No modo de sistema, pode-se verificar com systemctl status multi-user.target
  • Como o contêiner é executado como serviço de usuário do systemd, é preciso ativar o linger para que ele possa iniciar mesmo sem o usuário logado
    • loginctl enable-linger
  • Se for necessário iniciar automaticamente após reinicialização do servidor, ativar o linger é obrigatório
  • Para que o systemd detecte um novo arquivo de serviço, execute o seguinte comando
    • systemctl --user daemon-reload
  • O início do contêiner e a verificação de status podem ser feitos tanto pelo systemd quanto pelo Podman
    • systemctl --user start test-db
    • systemctl --user status test-db
    • podman ps
  • O nome padrão do contêiner recebe o prefixo systemd- antes do nome do arquivo de serviço
    • O nome padrão do contêiner para test-db.container é systemd-test-db
    • Essa convenção evita conflitos
    • Também é possível definir isso manualmente com a opção ContainerName na seção [Container]

Vantagens operacionais do Quadlet

  • O Quadlet permite gerenciar a configuração do serviço de contêiner em um único arquivo
    • Diferentemente do método antigo, não é preciso manter separadamente um script para gerar o arquivo de serviço e o próprio arquivo gerado
  • É possível usar as opções disponíveis nas seções [Unit] e [Service] do systemd
    • Por exemplo, é possível definir com StartExecPre um comando a ser executado antes da inicialização do contêiner
    • Isso reduz a necessidade de editar manualmente arquivos gerados depois
  • Em vez de escrever e depurar scripts de shell, é possível operar contêineres com foco em arquivos de configuração
  • Também fica mais fácil expressar dependências entre contêineres no estilo do systemd

Expressando dependências entre contêineres

  • Se um contêiner de aplicação depende de um contêiner de banco de dados, a relação pode ser definida na seção [Unit] do systemd
  • O contêiner OxiTraffic é um exemplo que depende do contêiner test-db
    • Em [Unit], definir Requires=test-db.service faz a aplicação iniciar somente quando o banco de dados for iniciado
    • Definir After=test-db.service evita que os dois contêineres iniciem em paralelo
  • Ao referenciar, usa-se o nome do serviço, não o nome do arquivo .container
    • test-db.container: nome do arquivo
    • test-db.service: nome do serviço
    • systemd-test-db: nome padrão do contêiner
  • Para que a aplicação se comunique com o banco de dados, ambos os contêineres precisam adicionar a opção Network na seção [Container], mas a parte de rede não é abordada aqui

Vários arquivos e agrupamento

  • O Quadlet pode ser organizado com um arquivo .container separado para cada contêiner
  • Em vez de colocar toda uma aplicação multi-contêiner em um único arquivo Docker Compose, ter um arquivo por contêiner pode reduzir a carga cognitiva
  • Quando um arquivo Docker Compose chega a centenas de linhas e dezenas de contêineres, a manutenção pode ficar difícil
  • O Docker Compose também oferece suporte para dividir a configuração em vários arquivos
  • O Quadlet permite colocar arquivos unit dentro de diretórios em ~/.config/containers/systemd
    • No exemplo, pode-se criar um diretório oxitraffic e manter juntos os arquivos da aplicação e do banco de dados

Atualização de imagens

  • Ao definir AutoUpdate=registry, é possível verificar atualizações de imagem com podman auto-update
  • Se houver uma nova imagem no registro compatível com a tag em uso, o Podman faz o pull da imagem e reinicia o contêiner
  • No Docker, pode ser necessário usar uma ferramenta como Watchtower para isso, mas o Podman oferece essa funcionalidade nativamente
  • Tags como latest podem ser arriscadas
    • No OxiTraffic, latest pode incluir breaking changes da próxima versão
    • No PostgreSQL, usar latest pode levar a uma nova versão major, e upgrades major do PostgreSQL sempre exigem migração manual
  • Em produção, deve-se usar tags que não levem a breaking changes
  • Também é possível executar podman auto-update manualmente a cada poucos dias, ver o que foi atualizado e depois verificar se os contêineres continuam funcionando normalmente

podman-compose e ferramentas de migração

  • podman-compose é um script em Python para executar arquivos Compose com Podman
  • Os motivos pelos quais é difícil vê-lo como uma alternativa de longo prazo ao Docker Compose são os seguintes
    • Ele é uma camada de tradução entre a especificação Compose e Podman/systemd, e não permite usar todos os recursos do systemd
    • O projeto oficial do Podman é escrito em linguagens compiladas como Rust ou Go
    • O último commit foi há 5 meses, então não parece estar sendo mantido ativamente
  • O Quadlet combina melhor com o desenho rootless e sem daemon do Podman
  • Para experimentar o Quadlet a partir de um arquivo Compose, é possível usar o podlet
    • Trata-se de uma ferramenta em Rust capaz de gerar arquivos Quadlet a partir de comandos Podman ou de arquivos Docker Compose

Leituras adicionais

1 comentários

 
GN⁺ 2025-03-25
Opiniões do Hacker News
  • Quadlet é uma das melhores coisas que saíram do Podman, e eu recomendo fortemente para quem tem curiosidade sobre Podman ou quer migrar para workloads baseados em contêineres
    Você consegue encaixar e tratar contêineres como serviços comuns do sistema, sem precisar aprender uma camada separada de orquestração para fazê-los funcionar juntos nem depender de recursos não conteinerizados
    Basta escrever as units do systemd como você já fazia, e atualizações automáticas ou reinício/notificação do serviço em caso de falha vêm de brinde
    A forma de tentar fazer algo parecido com Docker muitas vezes contornava o daemon do Docker com um comando run enorme, deixando serviços e contêineres fantasmas para trás, enquanto o Quadlet é muito mais limpo e as configurações ficam junto dos locais de units do systemd, como /etc/systemd/, .config/systemd e /usr/local/lib/systemd, facilitando o backup
    No entanto, ele não é a resposta ao docker-compose no desenvolvimento local, e a equipe do Podman também não parece muito interessada nessa área
    Contêineres de usuário são bons para infraestrutura local de teste de longa duração, como bancos de dados em segundo plano, mas são trabalhosos demais para o ciclo comum de compilar → docker compose up → testar → docker compose down
    A resposta prática é usar Quadlet .kube (Kubernetes play) ou docker compose apontando para o socket do Podman
    Gostei tanto que venho criando, no tempo livre há alguns meses, uma ferramenta GitOps para gerenciar Quadlet, e isso parece a forma correta de administrar servidores conteinerizados
    É importante que [0] não seja podman-compose. O texto também apontou que o podman-compose é ruim e carece de desenvolvimento. Como o Podman implementa a maior parte da especificação do compose, dá para usar docker compose na maioria das situações
    É bem provável que pessoas que tentaram usar quando a RH começou a impulsionar o Podman tenham desistido por causa das indefinições do Podman 3 e da experiência ruim com o podman-compose

    • Eu queria seguir algo mais próximo do jeito da RH e tentei usar Quadlet e podlet, mas a experiência de migrar do docker compose sobre Podman foi horrível
      Depois de mudar várias flags, atualizar versões e seguir o fluxo em que o próprio podlet recomendava trocar para outra ferramenta, isso virou uma toca de coelho sem fim
      No fim, meu systemd simplesmente executa podman compose up
      Fico curioso para saber qual é a vantagem do Quadlet em comparação a deixar o compose fazer o próprio trabalho
      Para referência, o Podman roda com meu usuário, sem daemon nenhum
    • A dica para desenvolvimento é ativar o socket do Podman e, se forem contêineres rootless, ativá-lo no escopo do usuário, então usar o docker-compose autônomo
    • No Windows ou no Mac, não dá para usar systemd, então no fim só resta podman compose
  • Descobri o Quadlet recentemente e montei todo o meu homelab com base em atomic OS e Quadlet rootless; recomendo muito
    Também é possível usar ativação por socket do systemd: por exemplo, criar sockets http/https do systemd e ativar automaticamente o Traefik, assim como ssh.socket e podman.socket ativam sshd.service e podman.service, respectivamente
    Em uma configuração rootless, é praticamente a única forma de preservar o IP de origem, então é quase um salva-vidas. Podman/Docker rootless normalmente não permitem preservar facilmente o IP de origem sem grandes desvantagens
    https://github.com/savely-krasovsky/homelab

    • Fico curioso para saber quanto tempo leva para o Traefik ser ativado
  • O Quadlet é uma boa alternativa para o uso do docker compose de “executar todos os contêineres interdependentes em um ambiente parecido com produção”
    Mas eu gostaria que ele fosse melhor no outro uso do docker compose, que é o desenvolvimento
    O docker compose permite subir juntos um banco de dados, Redis, OpenSearch, outras dependências, um proxy nginx e um contêiner de desenvolvimento com .:/app montado como volume; derrubar tudo junto; e colocar o docker-compose.yml no repositório
    Como o Quadlet manda colocar todos os arquivos em ~/.config/containers/systemd, ele deixa de ficar isolado ao projeto e também não é cômodo fazer check-in e compartilhar com outros desenvolvedores. Além disso, eles também teriam que usar Podman
    A maioria ainda usa Docker, e ambientes de desenvolvimento hospedados como o Codespaces também fornecem Docker
    Por isso uso docker compose com um arquivo YAML versionado. Como eu uso Podman, preciso adicionar manualmente :Z a todos os volumes, mas o Docker comum não consegue lidar com isso
    Seria bom ter uma alternativa ao docker compose para desenvolvimento, mas o Quadlet não parece se encaixar bem

    • Usando podman-compose --in-pod=1 systemd -a create-unit, dá para criar um serviço podman-compose@ e, depois, registrar um arquivo compose.yml junto com $name usando podman-compose systemd -a register
      Assim é possível gerenciar um pod baseado em arquivo compose como podman-compose@$name.service, e isso também funciona de forma totalmente rootless
    • Nesse caso, o que você procura é um Podman pod, e eu orquestraria tudo isso com Ansible e depois distribuiria o playbook do Ansible para todos executarem localmente
    • systemd-run permite executar um serviço systemd temporário com base em um comando arbitrário
      Se necessário, ele cria uma unidade temporária limpa, seguindo as regras de escopo do systemd, até com timer
      [1] https://www.freedesktop.org/software/systemd/man/systemd-run...
    • Vejo o Quadlet mais como algo para deploy. Se estiver procurando uma alternativa ao compose, é melhor olhar para o Podman Kube Play[1]
      Ele usa um formato parecido com Kubernetes, com algumas conveniências adicionais como build de contêineres
      Se você colocar o arquivo kube play na raiz do projeto e executar podman kube play project.yaml --build, o pod, os volumes etc. são executados juntos
      Arquivos Quadlet são úteis na etapa de deploy. Se quiser colocar o projeto em uma VPS, dá para usar Kube Quadlet[2] junto com o project.yaml que você já tem
      [1] https://docs.podman.io/en/latest/markdown/podman-kube-play.1...
      [2] https://docs.podman.io/en/latest/markdown/podman-systemd.uni...
    • Na minha empresa também usamos docker-compose.yml para desenvolvimento, então entendo a dor
      Em geral funciona muito bem, mas no OSX às vezes se comporta de forma estranha por causa daquela maldita VM intermediária
      De vez em quando verifico a compatibilidade com Podman, mas hoje aceitei que a filosofia rootless é diferente demais para haver compatibilidade completa
      Ainda assim, essa ferramenta é ótima para produção. Tenho alguns projetos hobby rodando em um nó bare metal com serviços systemd feitos à mão; atualmente é containerd em modo de usuário
      Quando eu tiver tempo, talvez eu migre
  • É curioso que o texto diga que o Podman-Compose “não é mantido ativamente”, apontando que o último commit foi há 5 meses, e logo em seguida recomende como alternativa o Podlet, cujo último commit também foi há 5 meses
    O Podlet pode ser útil, mas não dá suporte a muitos recursos do Docker Compose e nem sempre faz uma conversão limpa
    Em especial, não dá suporte ao recurso de empilhar vários arquivos YAML, como em -f docker-compose.yml -f docker-compose.override.yml

    • Se você gosta de arquivos compose, pode usar a aplicação compose do Docker[1] junto com o Podman[2]
      Parece que a CLI do compose controla o engine via socket, e os engines do Podman e do Docker têm APIs quase iguais
      Uso esse método porque o podman-compose não funcionou como eu esperava
      O Docker-compose normalmente é instalado como um plugin do cliente docker, mas eu o uso como uma aplicação independente para usar com o Podman
      Além disso, para a configuração de integração, prefiro docker contexts à variável de ambiente DOCKER_HOST
      Se apenas Quadlet puro não for poderoso o bastante, Quadlet[3] e o Podman comum[4] também dão suporte à execução limitada de manifests Kubernetes
      Ainda não entendi como o Podman lida com a opção restart dos arquivos compose. Isso porque o Podman não tem um daemon supervisor
      Por outro lado, sei que a opção healthcheck depende de timers do systemd
      Ao usar Podman no Gentoo, uma distribuição que não usa systemd, os healthchecks automáticos não funcionaram, mas, ao executar o healthcheck manualmente, o restante da configuração prosseguiu até o fim
      [1] https://github.com/docker/compose
      [2] https://docs.podman.io/en/latest/markdown/podman-system-serv...
      [3] https://docs.podman.io/en/latest/markdown/podman-systemd.uni...
      [4] https://docs.podman.io/en/latest/markdown/podman-kube-play.1...
    • Para ser justo, os recursos do Podlet não precisam de atualizações constantes, mas o podman compose precisa
      O Podlet é uma ferramenta auxiliar e, no longo prazo, é melhor trabalhar com Quadlet
    • Datas de commit não são precisas para mostrar que algo está sendo mantido, mas podem mostrar que não está sendo mantido
      5 meses não significa que algo não seja mantido; para isso, teria que ser algo em torno de 2 anos
      Ainda assim, isso aumenta a preocupação. Mas 5 meses definitivamente não são prova de que está sendo mantido
      Há muitos motivos para um repositório sem manutenção receber updates, e só o recente e meio ruim recurso de arquivamento do GitHub impede isso
  • Há alguns anos, ao recriar minha configuração de self-hosting, eu queria tentar algo diferente e escolhi o openSUSE MicroOS; acabei rodando contêineres Podman sob systemd/Quadlet e estou bem satisfeito com a configuração atual
    Os contêineres são atualizados automaticamente pelas ferramentas embutidas do Podman, e a consulta de logs e o monitoramento são feitos com as ferramentas habituais do systemd
    Quando preciso mudar algo, mesmo que eu tenha esquecido onde ficam os arquivos de configuração, é fácil encontrá-los, lê-los e modificá-los
    Também é bom ser rootless e daemonless
    No meio do caminho, experimentei algumas coisas, mas podman compose pareceu desajeitado; fico feliz que tenha sido deprecated, e está claro que Quadlet é o caminho a seguir
    É preciso levar em conta que há uma curva de aprendizado e menos material do que para Docker
    Para desenvolvimento local, se a ideia for subir e derrubar stacks com vários serviços, eu ainda escolheria Docker e docker compose

    • Estou rodando Nextcloud com Fedora CoreOS em uma workstation antiga e barata
      Levei algum tempo para acertar a configuração, mas fiquei muito impressionado com o fato de quase não exigir manutenção. Até agora, nenhuma
      Quem quiser fazer algo parecido pode consultar minha configuração
      https://github.com/jeppester/coreos-nextcloud
    • A combinação Quadlet + MicroOS é muito poderosa, e o mesmo vale para outras distribuições atomic, como Fedora CoreOS
      Estou migrando lentamente todos os nós para MicroOS e, na empresa, também estamos promovendo MicroOS ou algo semelhante
      A combinação de rollback automático do sistema operacional base com configuração declarativa de contêineres e updates automáticos parece se encaixar perfeitamente
  • O formato é mais claro que podman generate systemd ou YAML do Kubernetes, e a integração com systemd também é excelente
    O que irrita é que o upstream do Podman não fornece repositórios para Debian/Ubuntu
    No Debian stable, fiquei preso na 4.3.1 e perdi muitos recursos novos; no fim, decidi voltar para o Docker compose

    • Encontrei o mesmo problema ao tentar usar Quadlet em um Raspberry Pi rodando Debian
      As soluções alternativas sugeridas eram compilar o Podman manualmente ou usar debian/testing
  • Espero sinceramente que essa nova abordagem ajude as pessoas a migrarem do Docker para o Podman
    Docker-Compose é um motivo pelo qual muita gente hesita em fazer a migração, e comigo foi assim também
    Para ser honesto, antes do Quadlet o Podman não tinha uma resposta adequada
    Se você hesitava em sair do Docker por causa do Docker-Compose, Podman com Quadlet é uma alternativa muito mais comparável
    Você provavelmente sentirá menos falta do Docker do que imagina, e ainda ganha uma melhoria de segurança ao executar contêineres rootless

    • Eu não vou migrar até ser possível ter vários ambientes Quadlet independentes
      Colocar tudo em ~/.config/quadlet/systemd não funciona
      Para desenvolvimento e testes, isso é essencial; hoje o Quadlet é estritamente uma solução de implantação
  • Gosto muito do Quadlet porque ele permite usar contêineres como serviços comuns do sistema
    Mas a experiência de usuário de contêineres rootless não combina bem com essa conceitualização
    Normalmente, serviços do sistema rodam como usuários do sistema na sessão systemd do sistema, mas serviços de contêineres rootless ficam na sessão systemd de usuário do usuário do sistema
    Seria bom poder executar Quadlet rootless dentro da sessão do sistema

    • Fico me perguntando se há alguma diferença prática em habilitar user-linger e executar como rootless via systemd de usuário
      Eu sempre fiz assim
    • Eu também gostaria de poder executar Quadlet rootless dentro da sessão do sistema
      Também seria bom poder executar Quadlet rootless com a opção DynamicUser=
      DynamicUser= era uma ótima forma de restringir privilégios de serviços do sistema, mas hoje não se encaixa bem com o Podman
  • É interessante ver o Quadlet na primeira página do Hacker News. Acho que ele não recebeu atenção suficiente
    Ygal e Valentin, do projeto, já enviaram um guest post sobre como executar o cliente de túnel do inlets. É parecido com Ngrok/Cloudflared, mas auto-hospedado, sem as limitações de SaaS
    https://inlets.dev/blog/2023/10/03/client-quadlet.html
    Eles puderam usar [kube] em vez de [container] para levar YAML padrão do Kubernetes, o que o torna bastante portável

  • Para quem tiver interesse, criei um template Ansible que mostra como a implantação com Quadlet ficou fácil recentemente
    GH: https://github.com/Mati365/hetzner-podman-bunjs-deploy