Docker para iniciantes: o que é e como usar na prática
O que é Docker, para que serve, seu primeiro Dockerfile, os comandos básicos e o Docker Compose — guia direto para quem nunca usou containers.
O dev novo no time acabou de clonar o repositório. Seguiu o README passo a passo. Rodou. Não funcionou. Versão diferente do Node. Biblioteca de sistema faltando. Variável de ambiente que ninguém documentou. Esse ciclo tem nome, "na minha máquina funciona", e é exatamente o problema que o Docker resolve.
Docker é uma ferramenta que empacota a sua aplicação junto com tudo de que ela precisa para rodar (runtime, bibliotecas de sistema, configuração) numa imagem, e executa essa imagem como um container: um processo isolado que se comporta igual no seu notebook, na máquina do colega e no servidor. Este guia é para quem nunca usou: o que é, os primeiros comandos, o primeiro Dockerfile e o Compose para subir app e banco juntos.
O que é Docker e para que serve
Quando você instala uma aplicação na sua máquina, ela depende de dezenas de coisas além do código: versão do runtime, bibliotecas do sistema operacional, variáveis de ambiente, arquivos de configuração. Tudo isso existe no seu computador, mas não necessariamente em produção, ou no notebook do colega.
Antes do Docker, a solução era documentar tudo e torcer. Ou usar máquinas virtuais completas, o que funciona, mas custa tempo de setup e memória: uma VM carrega um sistema operacional inteiro só para rodar uma aplicação.
O Docker faz diferente. Em vez de virtualizar o hardware, ele isola processos usando recursos do próprio kernel Linux: namespaces (o que o processo enxerga: arquivos, rede, outros processos) e cgroups (quanto de CPU e memória ele pode usar). O resultado é um container, um processo isolado que só vê o que você definiu, mas compartilha o kernel da máquina host. Mais leve que uma VM, mais previsível que instalar direto na máquina.
Na prática, o Docker serve para três coisas no dia a dia de um dev: padronizar o ambiente de desenvolvimento (todo mundo roda a mesma versão de tudo), subir dependências como Postgres e Redis com um comando, sem instalar nada na máquina, e entregar para produção exatamente o mesmo artefato que passou no CI. Se você quer ver onde isso se encaixa no ciclo maior de deploy e automação, O que é DevOps além das ferramentas cobre a cultura e os processos por trás. E como quase toda imagem roda Linux por dentro, vale ter o básico de terminal do Linux para iniciantes.
Imagem, container e Docker Hub em um parágrafo
Imagem é o pacote imutável: o sistema de arquivos com a aplicação e suas dependências, mais os metadados de como executá-la. Container é a imagem em execução, com um processo rodando e uma camada gravável própria; uma imagem pode gerar dez containers ao mesmo tempo. O Docker Hub é o registry público de onde vêm as imagens oficiais (node, postgres, python). Essa distinção confunde todo iniciante e tem post próprio, com o ciclo de vida completo e o que acontece com os dados quando o container morre: imagem Docker vs container.
Primeiros comandos Docker
Instale o Docker Desktop (Windows e Mac) ou o Docker Engine (Linux) pela documentação oficial. Depois, confira se está tudo de pé:
docker --version
# Docker version 29.7.2, build a7dcaa6
docker run hello-world
# Hello from Docker!
# This message shows that your installation appears to be working correctly.
O docker run hello-world já faz o ciclo inteiro: procura a imagem localmente, não acha, baixa do Docker Hub, cria um container, executa e encerra. Os comandos que você vai usar a semana toda:
| Comando | Para que serve |
|---|---|
docker images |
lista as imagens baixadas ou construídas |
docker ps |
lista os containers rodando agora |
docker ps -a |
lista todos, inclusive os parados |
docker run -d --name api -p 3000:3000 minha-api:1.0 |
cria e sobe um container em segundo plano |
docker logs api (ou -f para acompanhar) |
mostra a saída do processo |
docker exec -it api sh |
abre um shell dentro do container rodando |
docker stop api |
para o container |
docker rm api |
remove o container parado |
docker rmi minha-api:1.0 |
remove a imagem |
Um detalhe que pega muita gente: docker stop não apaga nada, o container continua existindo (aparece no docker ps -a) até você rodar docker rm. Se quiser que ele suma sozinho ao parar, suba com docker run --rm.
Seu primeiro Dockerfile
O Dockerfile é um arquivo de texto com as instruções para construir a imagem. Um exemplo para uma API Node:
FROM node:26-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "src/index.js"]
Linha por linha:
FROM node:26-slimparte de uma imagem base oficial com o Node 26 em cima de um Debian enxuto. A versão importa: o Node 20 chegou ao fim de vida em 30/04/2026 e não recebe mais correções de segurança. Segundo o calendário oficial de releases do Node.js (consultado em 09/11/2026), o 26 é o LTS ativo desde 28/10/2026, com suporte até 30/04/2029; o 24 está em manutenção até 30/04/2028 e também é uma escolha segura. Use uma versão em LTS e troque antes do fim de vida, não depois.WORKDIR /appdefine (e cria) o diretório de trabalho dentro da imagem.COPY package*.json ./seguido deRUN npm ci --omit=devcopia só o manifesto e o lockfile e instala as dependências de produção. O--omit=devé a forma atual; a antiga--only=productionainda funciona, mas o npm 11 avisanpm warn config only Use --omit=dev.COPY . .copia o resto do código depois das dependências, de propósito.EXPOSE 3000documenta a porta que o processo usa. Não publica nada sozinho.CMDé o comando executado quando o container sobe.
Sobre a imagem base: -slim é o ponto de partida seguro. A variante -alpine é menor, mas usa a libc musl em vez da glibc, e dependências com binário nativo podem quebrar de formas sutis. Use Alpine quando tiver medido o ganho e testado a aplicação nela. A comparação completa está em como reduzir o tamanho de imagens Docker.
Crie também um .dockerignore com node_modules, .git e .env, para o COPY . . não arrastar a sua pasta local de dependências nem segredos para dentro da imagem. Para construir e rodar:
docker build -t minha-api:1.0 .
docker run -d --name api -p 3000:3000 minha-api:1.0
curl http://localhost:3000
O -p 3000:3000 mapeia a porta do host (à esquerda) para a do container (à direita). Sem ele, o processo roda, mas fica inacessível de fora. Se a 3000 já estiver ocupada na sua máquina, troque só o lado esquerdo: -p 8080:3000.
Por que a ordem do Dockerfile importa: o cache de camadas
As instruções que mexem no sistema de arquivos (principalmente RUN, COPY e ADD) geram camadas; as demais, como ENV, EXPOSE e CMD, só gravam metadados. No build seguinte, o Docker reaproveita o cache instrução por instrução até encontrar a primeira que mudou, e refaz dali para baixo.
Por isso o package.json vem antes do código. Rodei o build acima, alterei uma linha em src/index.js e rodei de novo: WORKDIR, COPY package*.json e RUN npm ci saíram como CACHED, e só o COPY . . foi refeito. Se o COPY . . viesse primeiro, qualquer mudança de código invalidaria tudo abaixo dele e o npm ci rodaria do zero a cada build.
Docker Compose: app e banco com um comando
Aplicação real tem pelo menos dois serviços: a API e o banco. O Compose descreve todos num arquivo compose.yaml (o nome antigo docker-compose.yml continua aceito):
services:
api:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://app:app@db:5432/myapp
depends_on:
db:
condition: service_healthy
db:
image: postgres:17
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: myapp
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d myapp"]
interval: 5s
timeout: 3s
retries: 5
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
docker compose up -d sobe tudo; docker compose down derruba. A API acessa o banco pelo hostname db, que é o nome do serviço, resolvido pela rede interna que o Compose cria.
Repare no condition: service_healthy. A armadilha clássica de iniciante é usar depends_on: [db] na forma curta, que só garante a ordem de subida: o Postgres ainda está inicializando quando a API tenta conectar, e ela cai. Com o healthcheck, o Compose espera o pg_isready responder antes de subir a API. Testei esse arquivo: o db ficou healthy e só então a api subiu. Há mais detalhes (hot-reload, profiles, override) em Docker Compose para desenvolvimento.
O pgdata é um volume nomeado: os dados do Postgres sobrevivem quando você recria o container. Só um docker compose down -v apaga o volume junto. Bind mount, volume nomeado e tmpfs têm diferenças que importam, explicadas em volumes Docker.
Por que isso resolve "na minha máquina funciona"
Porque o ambiente deixa de ser implícito. Antes, ele vivia em READMEs desatualizados e na memória de quem configurou o servidor. Com o Dockerfile, ele está no repositório, versionado junto com o código. O CI e a produção rodam a mesma imagem, então o que passou no CI é o que vai para o ar.
Não elimina todos os bugs de ambiente: arquitetura diferente (x86 vs ARM), segredos gerenciados fora do Compose e APIs externas ainda divergem. Mas elimina a classe mais comum: runtime na versão errada e dependência de sistema faltando. Quando a imagem for para produção, o próximo passo é endurecê-la (usuário não-root, segredos fora da imagem, scan de vulnerabilidades), o tema de boas práticas de Dockerfile.
Perguntas frequentes
Docker substitui máquina virtual?
Para rodar aplicações web, APIs e bancos em desenvolvimento e deploy, na maioria dos casos sim: containers sobem em segundos, usam menos memória e são descartáveis. VMs continuam fazendo sentido quando você precisa de outro kernel ou sistema operacional, ou de um isolamento mais forte do que o compartilhamento de kernel oferece.
Docker funciona igual no Windows, Mac e Linux?
No Linux, roda nativo e os containers compartilham o kernel da máquina. No Mac e no Windows, o Docker Desktop roda uma VM Linux leve em segundo plano (no Windows, via WSL 2) para fornecer esse kernel. No dia a dia é transparente, com alguma diferença de desempenho de I/O em bind mounts. Em produção o host é quase sempre Linux.
Qual a diferença entre docker run e docker compose?
docker run sobe um container de cada vez, com todas as opções na linha de comando. docker compose lê um arquivo que descreve vários serviços, suas redes, volumes e dependências, e sobe tudo junto. Para um container avulso, run; a partir de dois serviços que conversam, Compose.
Devo usar Docker em desenvolvimento local?
Se o projeto tem backend, banco e mais de uma pessoa, sim: o Compose economiza horas de onboarding. Para um script pessoal ou uma ferramenta simples que só você usa, o ganho é menor no começo. A conta inverte quando o projeto cresce ou outro dev precisa rodar.
O Dockerfile é a documentação do ambiente
O maior benefício do Docker não é velocidade de deploy: é tornar o ambiente explícito. Um Dockerfile bem escrito responde às perguntas que antes ficavam na cabeça de alguém: qual versão do runtime, quais dependências de sistema, qual porta, qual comando. Comece com docker run hello-world, escreva um Dockerfile com base -slim numa versão LTS suportada, ordene as instruções para o cache trabalhar a seu favor e use o Compose com healthcheck quando houver banco.
O conteúdo editorial termina aqui. Quando um compose.yaml não sobe por erro de indentação, além do docker compose config (que valida e imprime o arquivo resolvido), costumo colar o YAML no conversor de YAML para JSON do Quick Tools: a estrutura em JSON deixa óbvio qual chave foi parar no nível errado.
- 01 Como investigar falta de espaço em disco no Linux Um método de diagnóstico para descobrir por que o disco está cheio: df para achar o mount, du para o diretório, e os casos que confundem como inodes e arquivos deletados.
- 02 Permissão 755, 775 e 644 no Linux: o que significa (chmod) 755 para pastas e scripts, 644 para arquivos, 775 quando o grupo também grava: veja o que cada número do chmod libera e por que trocar o 777.