Você já ouviu um desenvolvedor dizer “funciona na minha máquina” e depois ver o mesmo código quebrar em produção. Esse problema tem nome, e Docker resolve boa parte dele.
Docker é uma plataforma que empacota uma aplicação junto com tudo o que ela precisa para rodar: bibliotecas, dependências, variáveis de ambiente e configurações do sistema. Esse pacote se chama container. Um container roda do mesmo jeito no notebook do desenvolvedor, no servidor de testes e na nuvem em produção.
Por Que Docker Existe
Antes dos containers, times instalavam aplicações direto no sistema operacional ou usavam máquinas virtuais para isolar ambientes. As duas abordagens trazem dor de cabeça.
Instalar direto no sistema gera conflito de versões. Um projeto precisa do Python 3.9, outro do 3.11, e os dois compartilham a mesma máquina. Máquinas virtuais resolvem o isolamento, mas cada VM carrega um sistema operacional completo, consome gigabytes de RAM e demora minutos para inicializar.
Docker isola aplicações sem duplicar o sistema operacional inteiro. Os containers compartilham o kernel do host e sobem em segundos, não minutos.
Container Não É Máquina Virtual

Essa confusão aparece o tempo todo, então vale separar os dois conceitos. Se quiser entender a fundo os critérios técnicos para escolher entre um e outro, escrevi um post dedicado só a isso: Docker ou Máquina Virtual: a decisão técnica que define o futuro do projeto.
Uma máquina virtual simula um computador completo: tem seu próprio kernel, seu próprio sistema operacional, seus próprios drivers. O hypervisor gerencia várias VMs isoladas dentro de um servidor físico.
Um container Docker usa o kernel do sistema operacional host. Ele isola apenas o espaço do processo, a rede e o sistema de arquivos da aplicação. Essa diferença explica por que containers pesam megabytes, não gigabytes, e por que iniciam quase instantaneamente.
| Característica | Máquina Virtual | Container Docker |
|---|---|---|
| Peso típico | Gigabytes | Megabytes |
| Tempo de inicialização | Minutos | Segundos |
| Sistema operacional | Um por VM | Compartilhado com o host |
| Isolamento | Total (hardware virtualizado) | De processo e sistema de arquivos |
Os Três Pilares do Docker
Imagem
Uma imagem é o molde do container. Ela contém o sistema de arquivos, as dependências e as instruções de como a aplicação deve rodar. Imagens são somente leitura e servem de base para criar quantos containers forem necessários.
Container
O container é a imagem em execução. Ele adiciona uma camada gravável por cima da imagem, onde ficam os dados gerados durante a execução. Quando o container para, essa camada some, a menos que você configure um volume.
Dockerfile
O Dockerfile é um arquivo de texto com as instruções para construir uma imagem. Cada linha vira uma camada na imagem final. Um exemplo simples para uma aplicação Node.js:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Comandos Essenciais para Começar
Esses comandos cobrem o dia a dia de quem está aprendendo Docker.
# Construir uma imagem a partir do Dockerfile
docker build -t minha-app .
# Rodar um container a partir da imagem
docker run -d -p 3000:3000 minha-app
# Listar containers em execução
docker ps
# Ver os logs de um container
docker logs
# Parar um container
docker stop
# Remover um container
docker rm
# Listar imagens locais
docker images
O comando docker run -d -p 3000:3000 minha-app merece atenção. A flag -d roda o container em segundo plano. A flag -p 3000:3000 mapeia a porta 3000 do container para a porta 3000 da sua máquina, permitindo acessar a aplicação pelo navegador.
Docker Compose: Múltiplos Containers Juntos
Aplicações reais raramente rodam sozinhas. Um projeto típico precisa de um backend, um banco de dados e talvez um cache. Docker Compose define todos esses serviços em um único arquivo YAML.
version: "3.8"
services:
app:
build: .
ports:
- "3000:3000"
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: senha123
volumes:
- dados_postgres:/var/lib/postgresql/data
volumes:
dados_postgres:
Com esse arquivo salvo como docker-compose.yml, o comando docker compose up sobe a aplicação e o banco juntos, já conectados na mesma rede.
Volumes: Guardando Dados Além do Container
Containers são descartáveis por natureza. Quando um container é removido, os dados escritos dentro dele somem junto. Para persistir dados, como um banco de dados, Docker oferece volumes.
Um volume vive fora do ciclo de vida do container. Você pode parar, remover e recriar o container quantas vezes quiser, e os dados no volume continuam intactos. O exemplo do Docker Compose acima já usa um volume chamado dados_postgres para garantir isso.
Quando Vale a Pena Usar Docker
Docker faz sentido em times que trabalham com múltiplos ambientes: desenvolvimento, testes, homologação e produção. Ele elimina a divergência entre esses ambientes, porque a mesma imagem roda em todos eles.
Também vale a pena em projetos com dependências complexas ou conflitantes entre si. Rodar um projeto legado em PHP 7 e um projeto novo em PHP 8 na mesma máquina, sem conflito, é trivial com containers.
Times que usam integração contínua se beneficiam ainda mais. Pipelines de CI/CD constroem, testam e implantam a aplicação dentro de containers idênticos aos de produção, reduzindo surpresas no deploy.
Erros Comuns de Quem Está Começando
Um erro frequente é usar imagens muito grandes como base, como ubuntu completo, quando uma imagem alpine resolveria o mesmo problema com uma fração do tamanho.
Outro erro é copiar todo o projeto para dentro da imagem antes de instalar as dependências. Isso invalida o cache de camadas do Docker a cada mudança de código, tornando o build lento. A ordem certa é copiar primeiro os arquivos de dependências (package.json, requirements.txt), instalar, e só depois copiar o restante do código, como no Dockerfile de exemplo acima.
Guardar senhas e chaves de API direto no Dockerfile também é um risco de segurança comum. Essas informações devem entrar por variáveis de ambiente ou por um arquivo .env, nunca hardcoded na imagem.