Todos os artigos
83 artigos · atualizado semanalmente Veja nossas Ferramentas
Todos os artigos
Comparativos

Imagem Docker vs container: qual é a diferença

Imagem é template imutável, container é instância. Entender a diferença — camadas, lifecycle e camada gravável — muda como você debugga e opera Docker.

COVER · Comparativos

A primeira vez que alguém tenta entender Docker, aprende que "você sobe um container". Mas quando a coisa quebra em produção e você precisa debugar, percebe que não sabe exatamente o que está rodando — se é a imagem, o container, outra coisa. A confusão tem origem num ponto concreto: imagem e container são coisas diferentes, e tratá-las como sinônimos cria problemas reais.

Se você já leu o guia de introdução ao Docker e quer entender o mecanismo mais fundo, este post é a continuação natural. Aqui o foco é na diferença entre imagem e container, como as camadas funcionam, e por que isso importa no dia a dia.


Imagem: o template imutável

Uma imagem Docker é um pacote somente leitura. Ela contém o sistema de arquivos da aplicação — o código, as dependências, as configurações, o binário do runtime — tudo empilhado em camadas. Uma vez construída, você não modifica uma imagem. Se precisar mudar algo, você reconstrói.

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0"]

Cada instrução no Dockerfile vira uma camada. FROM puxa a camada base, RUN adiciona uma camada com o resultado do comando, COPY adiciona outra com os arquivos copiados. Essas camadas são imutáveis e identificadas por hash.

docker image inspect minha-api:v1.0 --format '{{json .RootFS.Layers}}'
# ["sha256:a3ed...","sha256:4c7e...","sha256:f891..."]

O que torna isso eficiente é o reuso. Se duas imagens compartilham a mesma camada base (python:3.12-slim), o Docker armazena aquela camada uma única vez no disco. Na prática: fazer docker pull de uma nova versão da sua API, quando só o código mudou, baixa apenas as camadas novas — não o Python inteiro de novo.

Union filesystem: como as camadas viram um sistema de arquivos único

O Docker usa um sistema de arquivos de união (OverlayFS no Linux moderno) para empilhar as camadas somente leitura e apresentar um sistema de arquivos único ao container. É como empilhar transparências: cada camada mostra apenas o que mudou em relação à anterior.

Camada 4: código da aplicação    ← mais recente, sobrepõe
Camada 3: dependências instaladas
Camada 2: SO base mínimo
Camada 1: scratch / FROM base    ← mais antiga, base

Na leitura, o OverlayFS busca o arquivo na camada mais recente que o contém. Se o arquivo existe em múltiplas camadas, a mais nova vence.


Container: a instância em execução

Container é o que acontece quando você pede docker run. O Docker pega a imagem (com todas as camadas somente leitura) e adiciona uma camada nova no topo — essa sim é gravável. O processo da aplicação roda dentro desse ambiente isolado, enxerga um sistema de arquivos completo, mas qualquer coisa que escrever vai para a camada gravável do container, não para a imagem.

docker run -d --name api-v1 minha-api:v1.0
docker run -d --name api-v2 minha-api:v1.0
docker run -d --name api-v3 minha-api:v1.0

Três containers, uma imagem. Cada container tem sua própria camada gravável, seu próprio processo, seu próprio namespace de rede — mas compartilham as mesmas camadas somente leitura da imagem no disco. Isso é o que torna containers leves em comparação a VMs: não há três cópias do sistema de arquivos, só três thin layers por cima do mesmo conjunto de camadas.

O que acontece com a camada gravável quando o container morre

Aqui está o ponto que mais confunde: quando o container é removido, a camada gravável some junto. Tudo que foi escrito no sistema de arquivos do container durante a execução — logs, arquivos temporários, dados gerados — desaparece.

docker rm api-v1
# A camada gravável de api-v1 foi destruída

Se a aplicação precisa persistir dados entre reinicializações, a resposta é volumes:

docker run -d \
  --name api-v1 \
  -v /data/api:/app/data \
  minha-api:v1.0

O volume existe fora do container. Quando o container morre e um novo sobe com o mesmo volume montado, os dados estão lá.


Lifecycle: o caminho completo

Entender o ciclo de vida ajuda a saber onde cada comando opera:

Dockerfile → docker build → imagem (local)
                             ↓
                        docker push → registry (remoto)
                             ↓
                        docker pull → imagem (local)
                             ↓
                        docker run  → container (rodando)
                             ↓
                        docker stop → container (parado)
                             ↓
                        docker rm   → container destruído
                             ↓
                        docker rmi  → imagem removida

Container parado não é container destruído. docker stop para o processo, mas a camada gravável ainda existe. Você pode docker start de novo e o estado do container (incluindo o que foi escrito na camada gravável) ainda está lá. Só docker rm destrói de vez.

# Inspecionar todos os containers, incluindo os parados
docker ps -a

# Ver quanto espaço as camadas de containers parados estão ocupando
docker system df

Por que essa distinção importa na prática

Imutabilidade da imagem como contrato

Quando você taga uma imagem com minha-api:v1.2.3 e faz push para o registry, aquela tag deve ser imutável em produção. Se você recriar a imagem com o mesmo nome e tag, você quebra o contrato — alguém que subiu v1.2.3 ontem está rodando algo diferente de quem sobe hoje. Use tags semânticas ou hashes de commit para deploy.

# Preferível em CI/CD
docker build -t minha-api:$(git rev-parse --short HEAD) .

Debugging: você quer um shell no container, não na imagem

Uma confusão comum é tentar "entrar na imagem". Imagem não tem processo rodando — você não entra nela. Você entra num container:

# Container já rodando
docker exec -it api-v1 /bin/bash

# Ou sobe um container temporário só pra inspecionar
docker run --rm -it minha-api:v1.0 /bin/bash

O --rm destrói o container quando você sai. Útil para inspeção pontual sem deixar lixo.

Limpeza de imagens vs containers

São operações separadas por um motivo:

# Remove containers parados
docker container prune

# Remove imagens sem container usando elas
docker image prune

# Remove tudo que não está em uso (containers, images, volumes, networks)
docker system prune -a

docker system prune -a vai remover imagens que você talvez queira manter em cache para o próximo build. Use com critério.


Perguntas frequentes

Qual a diferença prática entre docker stop e docker rm?

docker stop envia SIGTERM para o processo principal do container e aguarda ele terminar (por padrão, 10 segundos antes de SIGKILL). O container para, mas continua existindo com sua camada gravável intacta. docker rm destrói o container — processo já deve estar parado, e a camada gravável some. Em automação (CI/CD, scripts), é comum encadear: docker stop nome && docker rm nome, ou usar docker rm -f nome que para e remove em um comando.

Posso transformar um container de volta em imagem?

Sim, com docker commit. Mas raramente é uma boa ideia fora de situações de debugging. O que você obtém é uma imagem gerada a partir da camada gravável do container — sem histórico de instruções, sem Dockerfile reproduzível, sem cache utilizável em builds futuros. O resultado é uma imagem opaca que ninguém sabe como foi construída.

docker commit api-v1 minha-api:debug-snapshot

Use para capturar estado de emergência. Para produção, sempre reconstrua a partir do Dockerfile.

Por que containers são mais rápidos de subir do que VMs?

Containers não virtualizam hardware nem inicializam um kernel completo — eles compartilham o kernel do host. O "boot" de um container é essencialmente iniciar um processo com namespaces e cgroups configurados. Uma imagem com aplicação Go compilada pode subir em menos de 200ms. Uma VM precisa inicializar BIOS, kernel, serviços de SO — ordem de segundos ou dezenas de segundos.


O que fica desta distinção

Imagem é template, container é instância. Imagem é imutável, container tem estado temporário. Imagem existe no registro e no cache local, container existe só na máquina onde está rodando. Essa separação é o que torna o Docker composível: você pode subir dez containers a partir da mesma imagem, matar nove, e o décimo continua usando o mesmo cache de camadas no disco.

Quando algo quebra em produção, saber se o problema está na imagem (foi buildada com o código errado?) ou no container (está com estado corrompido na camada gravável? perdeu acesso ao volume?) faz diferença na velocidade do diagnóstico.

O próximo passo natural é entender como redes e volumes funcionam para conectar containers entre si — mas isso é outro post.

RD
Autor
Rafael Duarte
Desenvolvedor backend com passagem por fintech e SaaS B2B — trabalhou em times que escalaram APIs de zero a milhões de requisições. Carrega cicatrizes de produção suficientes para ter opiniões fortes sobre ferramentas, padrões e decisões de arquitetura. Não é acadêmico: leu a RFC do UUID quando precisou escolher entre v4 e v7 para uma tabela de alta escrita.
Ver perfil