De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.
Colaboração: Rubens Queiroz de Almeida
Data de Publicação: 13 de agosto de 2026
Ao trabalhar com contêineres, podemos baixar imagens prontas de registros como Docker Hub e Quay.io. Entretanto, muitas vezes precisamos criar uma imagem personalizada, contendo nossa aplicação, arquivos de configuração e dependências.
No Podman, essa imagem pode ser criada por meio de um arquivo chamado Containerfile. Ele é um arquivo de texto que descreve, passo a passo, como a imagem deve ser construída.
Neste tutorial, criaremos uma imagem com o servidor Apache e uma página HTML personalizada. Depois, construiremos a imagem, iniciaremos um contêiner, examinaremos seu conteúdo e veremos as principais instruções utilizadas em um Containerfile.
Um Containerfile contém instruções para construir uma imagem de contêiner. Entre outras coisas, ele permite definir:
O Podman aceita tanto o nome Containerfile quanto Dockerfile.
A sintaxe interna é a mesma. Isso significa que, na maioria dos casos, um Dockerfile criado para o Docker pode ser usado pelo Podman sem modificações. A documentação do Podman confirma que os dois nomes são reconhecidos pelo comando podman build. Consulte o manual oficial do Podman.
O nome Containerfile é mais neutro, pois não vincula o arquivo a uma ferramenta específica.
Antes de começarmos, é importante distinguir os dois conceitos.
Uma imagem é um modelo imutável que contém os arquivos necessários para executar uma aplicação. Um contêiner é uma instância em execução dessa imagem.
Podemos criar vários contêineres a partir da mesma imagem:
flowchart TD A["Imagem personalizada"] --> B["Contêiner 1"] A --> C["Contêiner 2"] A --> D["Contêiner 3"]
O Containerfile cria a imagem. O comando podman run cria e inicia um contêiner usando essa imagem.
Execute:
$ podman --version
A saída será semelhante a:
$ podman version 5.7.0
Também podemos verificar as informações do ambiente:
$ podman info
Na maioria das distribuições, o Podman pode ser instalado diretamente pelo gerenciador de pacotes.
No Fedora:
$ sudo dnf install podman
No Ubuntu e no Debian:
$ sudo apt install podman
No openSUSE:
$ sudo zypper install podman
Crie um diretório para o exemplo:
mkdir meu-site
Entre nele:
cd meu-site
Ao final, teremos a seguinte estrutura:
meu-site/ ├── Containerfile ├── index.html └── .containerignore
Crie o arquivo index.html com o seguinte conteúdo:
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Meu primeiro Containerfile</title>
<style>
body {
background: #f4f4f4;
color: #333;
font-family: Arial, sans-serif;
margin: 0;
}
main {
background: white;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
margin: 80px auto;
max-width: 700px;
padding: 40px;
text-align: center;
}
h1 {
color: #7c3aed;
}
</style>
</head>
<body>
<main>
<h1>Minha primeira imagem</h1>
<p>
Esta página está sendo servida por um contêiner
criado com Podman e Containerfile.
</p>
</main>
</body>
</html>
</pre>
Esse arquivo substituirá a página padrão do Apache.
Crie um arquivo chamado exatamente Containerfile.
Adicione o seguinte conteúdo:
FROM docker.io/library/httpd:2.4-alpine LABEL org.opencontainers.image.title="Meu site Apache" LABEL org.opencontainers.image.description="Imagem de exemplo criada com Podman" LABEL org.opencontainers.image.version="1.0" COPY index.html /usr/local/apache2/htdocs/index.html EXPOSE 80
Esse é um Containerfile pequeno, mas suficiente para produzir uma imagem funcional.
A instrução:
FROM docker.io/library/httpd:2.4-alpine
define a imagem-base.
Nesse caso, estamos utilizando a imagem oficial do Apache HTTP Server, identificada como httpd. A variante alpine utiliza Alpine Linux como base e geralmente ocupa menos espaço do que imagens baseadas em distribuições maiores.
A referência completa contém:
docker.io/library/httpd:2.4-alpine
Seus componentes são:
| Componente | Significado |
|---|---|
docker.io |
Registro em que a imagem está armazenada |
library |
Namespace das imagens oficiais |
httpd |
Nome do repositório |
2.4-alpine |
Tag da imagem |
O uso do nome completo evita dúvidas sobre o registro em que o Podman deverá procurar a imagem.
As instruções:
LABEL org.opencontainers.image.title="Meu site Apache" LABEL org.opencontainers.image.description="Imagem de exemplo criada com Podman" LABEL org.opencontainers.image.version="1.0"
acrescentam metadados à imagem.
Esses dados não interferem diretamente na execução do Apache, mas ajudam a documentar:
As chaves usadas no exemplo seguem as convenções da Open Container Initiative, OCI.
A instrução:
COPY index.html /usr/local/apache2/htdocs/index.html
copia o arquivo index.html do diretório do projeto para dentro da imagem.
O primeiro caminho index.html é relativo ao contexto da construção.
O segundo /usr/local/apache2/htdocs/index.html é o destino dentro da imagem.
Na imagem oficial httpd, o diretório /usr/local/apache2/htdocs é usado para armazenar os arquivos publicados pelo Apache.
A instrução:
EXPOSE 80
documenta que a aplicação utiliza a porta 80.
É importante entender que EXPOSE não publica automaticamente a porta no computador hospedeiro. A publicação será feita ao iniciar o contêiner:
$ podman run -p 8080:80
Quando executamos:
$ podman build .
o ponto no final representa o diretório atual. Esse diretório se transforma no contexto da construção.
O contexto é o conjunto de arquivos que o Podman pode acessar durante o processo. Instruções como COPY e ADD somente conseguem acessar arquivos que estejam dentro desse contexto.
Por exemplo, isto funciona se index.html estiver no diretório atual:
COPY index.html /var/www/html/index.html
Entretanto, não devemos tentar copiar um arquivo localizado fora do contexto:
COPY ../index.html /var/www/html/index.html
O ponto no comando abaixo, portanto, tem uma função importante:
$ podman build -t localhost/meu-site:1.0 .
Ele não deve ser esquecido.
Nem todos os arquivos existentes no diretório do projeto precisam fazer parte do contexto.
Crie o arquivo .containerignore.
Adicione:
.git .gitignore *.log *.tmp *.tar *.tar.gz README.md
O Podman deixará esses arquivos fora do contexto da construção.
Isso ajuda a:
O Podman também reconhece .dockerignore. Quando os dois arquivos existem, .containerignore tem precedência. Nunca mantenha senhas, chaves privadas ou credenciais dentro do contexto sem necessidade.
Execute:
$ podman build \
-t localhost/meu-site:1.0 \
.
A opção -t atribui um nome e uma tag à imagem:
localhost/meu-site:1.0
Os componentes são:
| Parte | Significado |
|---|---|
localhost |
Indica uma imagem local |
meu-site |
Nome da imagem |
1.0 |
Tag ou versão |
Durante a construção, o Podman executará aproximadamente estas etapas:
Containerfile;
httpd:2.4-alpine, caso ela ainda não exista;
index.html;
localhost/meu-site:1.0.
Ao final, uma mensagem indicará que a imagem foi criada.
Execute:
$ podman images
A saída será semelhante a:
REPOSITORY TAG IMAGE ID CREATED SIZE localhost/meu-site 1.0 a1b2c3d4e5f6 1 minute ago 58 MB docker.io/library/httpd 2.4-alpine 7f8e9d0c1b2a 3 days ago 57 MB
Também podemos procurar somente nossa imagem:
$ podman images localhost/meu-site
Crie e inicie um contêiner:
$ podman run -d \
--name meu-site \
-p 8080:80 \
localhost/meu-site:1.0
As opções utilizadas são:
| Opção | Função |
|---|---|
-d |
Executa o contêiner em segundo plano |
--name meu-site |
Define um nome para o contêiner |
-p 8080:80 |
Liga a porta 8080 do host à porta 80 do contêiner |
localhost/meu-site:1.0 |
Define a imagem utilizada |
Confira o contêiner:
$ podman ps
A saída mostrará algo parecido com:
CONTAINER ID IMAGE COMMAND STATUS PORTS 123456789abc localhost/meu-site:1.0 httpd-foreground Up 1 minute 0.0.0.0:8080->80/tcp
Abra no navegador:
http://localhost:8080
Também podemos testar pelo terminal:
curl http://localhost:8080
O código HTML da página deverá ser exibido.
Para conferir somente o código HTTP:
curl -I http://localhost:8080
A resposta deverá conter:
HTTP/1.1 200 OK
Para visualizar as mensagens do Apache:
$ podman logs meu-site
Para acompanhar os logs continuamente:
$ podman logs -f meu-site
Pressione Ctrl+C para encerrar o acompanhamento. Isso não interromperá o contêiner.
Podemos executar um shell dentro do contêiner:
$ podman exec -it meu-site /bin/sh
Como a imagem utiliza Alpine Linux, o shell disponível é normalmente /bin/sh, e não necessariamente /bin/bash.
Dentro do contêiner, verifique o arquivo:
$ ls -l /usr/local/apache2/htdocs
Examine seu conteúdo:
$ cat /usr/local/apache2/htdocs/index.html
Para sair:
$ exit
Execute:
$ podman inspect localhost/meu-site:1.0
O resultado será um documento JSON bastante extenso.
Para consultar somente os rótulos:
$ podman inspect \
--format '{{json .Labels}}' \
localhost/meu-site:1.0
Para visualizar as portas declaradas:
$ podman inspect \
--format '{{json .Config.ExposedPorts}}' \
localhost/meu-site:1.0
Para consultar a arquitetura:
$ podman inspect \
--format '{{.Architecture}}' \
localhost/meu-site:1.0
Para obter o identificador da imagem:
$ podman inspect \
--format '{{.Id}}' \
localhost/meu-site:1.0
Uma imagem de contêiner é formada por camadas. Para examinar o histórico:
$ podman history localhost/meu-site:1.0
A saída mostrará as instruções que contribuíram para sua construção.
A imagem-base já possui várias camadas. As instruções do nosso Containerfile acrescentam novas informações sobre elas.
Podemos representar o resultado desta forma:
flowchart TD A["Imagem Alpine Linux"] --> B["Apache HTTP Server"] B --> C["Metadados LABEL"] C --> D["Página index.html"] D --> E["Imagem meu-site:1.0"]As camadas podem ser compartilhadas por diferentes imagens. Se duas imagens usam a mesma base, o Podman não precisa guardar duas cópias completas dela.
Modifique o conteúdo de index.html. Por exemplo:
<h1>Minha imagem foi atualizada</h1>
A imagem já criada não será alterada automaticamente. Precisamos construí-la novamente.
Podemos criar uma nova versão:
$ podman build \
-t localhost/meu-site:1.1 \
.
Liste as versões:
$ podman images localhost/meu-site
Agora deverão existir duas tags:
localhost/meu-site 1.1 localhost/meu-site 1.0
Pare e remova o contêiner antigo:
$ podman stop meu-site $ podman rm meu-site
Inicie a nova versão:
$ podman run -d \
--name meu-site \
-p 8080:80 \
localhost/meu-site:1.1
Atualize a página no navegador.
Esse processo demonstra uma propriedade importante: a imagem 1.0 continua existindo. A reconstrução com outra tag gerou uma nova versão, permitindo retornar à anterior se necessário.
Durante a segunda construção, provavelmente algumas etapas serão executadas rapidamente porque o Podman reaproveitará resultados anteriores.
Se a imagem-base e as instruções não mudaram, suas camadas podem ser recuperadas do cache.
Quando alteramos apenas o index.html, as etapas anteriores ao COPY podem continuar sendo reutilizadas. O COPY e as instruções posteriores precisarão ser avaliados novamente.
Por isso, a ordem das instruções influencia a eficiência da construção. As etapas que mudam raramente devem aparecer antes das etapas que mudam com frequência.
Quando for necessário ignorar o cache:
$ podman build \
--no-cache \
-t localhost/meu-site:1.1 \
.
Para procurar uma versão atualizada da imagem-base:
$ podman build \
--pull=always \
-t localhost/meu-site:1.1 \
.
Recriar imagens regularmente ajuda a incorporar correções de segurança presentes na imagem-base.
Além das instruções utilizadas no exemplo, existem outras muito importantes.
Executa um comando durante a construção da imagem:
RUN apk add --no-cache curl
Esse comando instala curl em uma imagem baseada no Alpine.
Em uma imagem baseada no Debian ou Ubuntu, poderíamos usar:
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*
Tudo o que for feito por RUN torna-se parte da imagem construída.
Define o comando padrão executado quando o contêiner é iniciado:
CMD ["httpd-foreground"]
Uma imagem pode ter somente um CMD efetivo. Se houver mais de um, prevalecerá o último.
O usuário pode substituir esse comando ao executar o contêiner:
$ podman run localhost/minha-imagem outro-comando
Define o programa principal do contêiner:
ENTRYPOINT ["/usr/local/bin/minha-aplicacao"]
Os argumentos passados ao podman run normalmente são acrescentados ao ENTRYPOINT.
Uma combinação possível é:
ENTRYPOINT ["/usr/local/bin/minha-aplicacao"] CMD ["--help"]
Nesse caso, --help funciona como argumento padrão.
Define o diretório de trabalho:
WORKDIR /app
As instruções seguintes serão executadas em relação a esse diretório:
WORKDIR /app COPY aplicacao.sh . RUN chmod +x aplicacao.sh CMD ["./aplicacao.sh"]
É preferível utilizar WORKDIR em vez de repetir comandos como:
RUN cd /app && algum-comando
Define uma variável de ambiente permanente:
ENV AMBIENTE=producao
Ela estará disponível dentro do contêiner:
$ podman run --rm localhost/minha-imagem env
Não use ENV para armazenar senhas, tokens ou chaves privadas, pois esses valores podem permanecer nos metadados e nas camadas da imagem.
Define uma variável usada somente durante a construção:
ARG VERSAO=1.0
Ela pode ser informada na linha de comando:
$ podman build \
--build-arg VERSAO=2.0 \
-t localhost/minha-imagem:2.0 \
.
No Containerfile:
ARG VERSAO
RUN echo "Construindo a versão ${VERSAO}"
Embora ARG seja voltado para a construção, também não deve ser considerado um mecanismo seguro para fornecer segredos.
Define o usuário utilizado pelas instruções seguintes e pelo processo do contêiner:
USER 1000
Quando a aplicação não precisa de privilégios administrativos, executá-la como usuário comum reduz os possíveis impactos de uma falha de segurança.
Assim como COPY, ADD acrescenta arquivos à imagem, mas possui comportamentos adicionais, como o tratamento de determinados arquivos compactados e origens remotas.
Para cópias comuns, prefira:
COPY arquivo.conf /app/arquivo.conf
O uso de COPY deixa a intenção mais clara.
Indica um ponto destinado a dados persistentes:
VOLUME ["/dados"]
A instrução não cria, por si só, uma política de backup. Os volumes ainda precisam ser administrados pelo Podman.
Define uma verificação de saúde para a aplicação:
HEALTHCHECK --interval=30s --timeout=5s \
CMD wget -q --spider http://localhost/ || exit 1
A verificação ajuda a detectar situações em que o processo ainda está ativo, mas a aplicação não está respondendo corretamente.
Podemos personalizar ainda mais a imagem do Apache:
FROM docker.io/library/httpd:2.4-alpine
LABEL org.opencontainers.image.title="Meu site Apache"
LABEL org.opencontainers.image.description="Apache personalizado com ferramentas de diagnóstico"
LABEL org.opencontainers.image.version="2.0"
RUN apk add --no-cache \
curl \
tzdata
ENV TZ=America/Sao_Paulo
COPY index.html /usr/local/apache2/htdocs/index.html
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl --fail http://localhost/ || exit 1
CMD ["httpd-foreground"]
Construa a imagem:
$ podman build \
-t localhost/meu-site:2.0 \
.
Teste o curl instalado:
$ podman run --rm \
localhost/meu-site:2.0 \
curl --version
Nesse caso, o comando informado substitui o CMD padrão.
Seria possível iniciar um contêiner, entrar nele e modificar seus arquivos:
$ podman exec -it meu-site /bin/sh
Entretanto, essas alterações ficariam somente na camada gravável daquele contêiner. Se ele fosse removido, as modificações seriam perdidas.
O Containerfile torna o processo:
Qualquer pessoa que receba o Containerfile e os arquivos do contexto poderá reconstruir uma imagem equivalente.
Podemos atribuir uma nova tag sem reconstruir a imagem:
$ podman tag \
localhost/meu-site:2.0 \
localhost/meu-site:latest
Confira:
$ podman images localhost/meu-site
As tags 2.0 e latest poderão apontar para o mesmo identificador de imagem.
Também podemos preparar a imagem para um registro:
$ podman tag \
localhost/meu-site:2.0 \
docker.io/meuusuario/meu-site:2.0
Para transportar a imagem sem utilizar um registro:
$ podman save \
-o meu-site-2.0.tar \
localhost/meu-site:2.0
Em outro computador, ela poderá ser carregada com:
$ podman load \
-i meu-site-2.0.tar
Use podman save e podman load para preservar a estrutura da imagem e suas camadas. podman export e podman import trabalham com o sistema de arquivos de contêineres e têm outra finalidade. Veja a distinção no manual do Podman.
Pare o contêiner:
$ podman stop meu-site
Remova-o:
$ podman rm meu-site
Remova uma versão específica da imagem:
$ podman rmi localhost/meu-site:1.0
Para remover todas as tags do exemplo, examine primeiro o que será afetado:
$ podman images localhost/meu-site
Depois, remova explicitamente as versões desejadas:
$ podman rmi \
localhost/meu-site:1.1 \
localhost/meu-site:2.0 \
localhost/meu-site:latest
Se duas tags apontarem para a mesma imagem, a remoção de uma tag não necessariamente eliminará imediatamente todas as camadas.
Mensagem semelhante:
no Containerfile or Dockerfile specified or found
Verifique o nome:
ls -l
O arquivo deve se chamar Containerfile ou Dockerfile.
Se possuir outro nome, informe-o com -f:
$ podman build \
-f Containerfile.apache \
-t localhost/meu-site:1.0 \
.
Arquivos com extensões adicionais não são encontrados automaticamente por podman build ..
Mensagem semelhante:
no such file or directory
Confira se o arquivo está dentro do contexto:
ls -l index.html
Verifique também se ele não foi excluído por .containerignore.
Mensagem semelhante:
address already in use
Descubra se existe outro contêiner usando a porta:
$ podman ps
Escolha outra porta no host:
$ podman run -d \
--name meu-site \
-p 8081:80 \
localhost/meu-site:1.0
Acesse:
http://localhost:8081
Pode existir um contêiner baseado na versão anterior da imagem.
Confira qual imagem ele utiliza:
$ podman inspect \
--format '{{.ImageName}}' \
meu-site
Remova e recrie o contêiner com a nova tag:
$ podman rm -f meu-site
$ podman run -d \
--name meu-site \
-p 8080:80 \
localhost/meu-site:1.1
Também pode ser necessário atualizar o cache do navegador.
Faça uma construção sem cache:
$ podman build \
--no-cache \
-t localhost/meu-site:1.1 \
.
Use essa opção para diagnóstico. Em condições normais, o cache reduz bastante o tempo de construção.
Ao criar imagens com Containerfile, procure seguir estes princípios:
latest;
.containerignore;
Containerfile junto ao código-fonte e sob controle de versão.
A escolha de uma imagem-base pequena e confiável, a exclusão de arquivos desnecessários e a instalação apenas das dependências necessárias estão entre as recomendações oficiais para produzir imagens mais seguras e eficientes. Consulte as boas práticas de construção.
O Containerfile transforma a criação de uma imagem em um processo documentado e reproduzível. Em vez de iniciar um contêiner e configurá-lo manualmente, descrevemos tudo o que ele precisa em um arquivo de texto.
No exemplo deste tutorial, partimos de uma imagem oficial do Apache, adicionamos metadados, copiamos uma página HTML e construímos nossa própria imagem:
$ podman build \
-t localhost/meu-site:1.0 \
.
Em seguida, criamos um contêiner:
$ podman run -d \
--name meu-site \
-p 8080:80 \
localhost/meu-site:1.0
Embora o exemplo seja simples, o mesmo princípio pode ser usado para criar imagens com PHP, Python, Java, bancos de dados, servidores web e aplicações completas. O que muda é o conteúdo do Containerfile. O processo básico permanece o mesmo: escolher uma imagem-base, acrescentar os componentes necessários, construir a imagem e testá-la em um contêiner.