Guides
Fundamentos ▾
Versionamento ▾
Deploy ▾

Docker

Plataforma de containerização que empacota aplicação + dependências em uma unidade isolada e portátil. Roda de forma idêntica em qualquer ambiente.

Container vs Máquina Virtual

🐳 Container
Startupsegundos
TamanhoMBs
Kernelcompartilhado
Isolamentoprocesso
Overheadmínimo
vs
🖥️ VM
Startupminutos
TamanhoGBs
Kernelpróprio (Guest OS)
Isolamentokernel completo
Overheadalto (hypervisor)

Container compartilha o kernel do host via namespaces + cgroups do Linux. Não emula hardware — muito mais leve.

Arquitetura

💻
Docker CLI
docker build · run · pull · push · compose
cliente
⚙️
Docker Daemon (dockerd)
Gerencia images, containers, volumes, networks via REST API (unix socket)
daemon
🔧
containerd + runc
Runtime de baixo nível. Executa containers de fato usando namespaces e cgroups do Linux.
runtime
🌐
Registry
Docker Hub · GHCR · ECR · registry privado
storage

🧠 Conceitos Fundamentais

🖼️

Image

Template read-only. Blueprint do container. Composta de layers imutáveis e compartilhadas.

📦

Container

Instância em execução de uma image. Isolado, efêmero por padrão. Dados perdidos ao remover — use volumes.

📄

Dockerfile

Script de instruções para construir uma image. Define base, comandos, arquivos, usuário, porta.

🗄️

Registry

Repositório de images. Docker Hub é o padrão público. GHCR, ECR, GCR para privados.

💾

Volume

Armazenamento persistente fora do ciclo de vida do container. Dados sobrevivem a docker rm.

🔀

Network

Rede virtual que conecta containers entre si e ao host. Containers na mesma rede se comunicam por nome.

Como images funcionam — layers

📱
Layer de escrita do container (R/W)
Única camada mutável. Descartada quando o container é removido.
container
📂
COPY . . (seu código)
Layer imutável. Compartilhada se o código for igual.
image
📦
RUN npm install
Cacheada se package.json não mudou. Por isso COPY package.json vem antes.
image
🏗️
FROM node:20-alpine
Base image. Compartilhada por todos os containers que usam a mesma base.
base

⚙️ Instalação

bash — Ubuntu / Debian
# Dependências e repositório
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Instalar
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Verificar
docker --version
docker compose version
bash — pós-instalação (rodar sem sudo)
sudo usermod -aG docker $USER
newgrp docker          # aplica sem fazer logout
docker run hello-world # testar
🖥️
macOS / Windows

Usar Docker Desktop — instala daemon, CLI e Docker Compose de uma vez. Download em docs.docker.com/desktop

🖼️ Images

bash
# Listar locais
docker images
docker image ls

# Baixar
docker pull nginx
docker pull nginx:1.25-alpine      # tag específica — sempre preferir
docker pull ubuntu:22.04

# Remover
docker rmi nginx
docker image rm nginx:1.25
docker rmi $(docker images -q)    # remover todas

# Inspecionar
docker inspect nginx
docker image history nginx         # ver layers e comandos

# Tag
docker tag minha-app:latest usuario/minha-app:1.0

# Buscar no Docker Hub
docker search nginx

# Limpar images não usadas
docker image prune                 # só dangling (sem tag)
docker image prune -a              # todas não usadas por container
⚠️
Nunca use :latest em produção

Tag inespecífica. Um pull pode trazer versão diferente e quebrar a aplicação. Use sempre nginx:1.25-alpine, node:20.11, etc.

📦 Containers

Comandos essenciais

bash
# Criar e iniciar
docker run nginx
docker run -d nginx                         # detached (background)
docker run -it ubuntu:22.04 bash            # interativo com TTY
docker run --name meu-nginx -d nginx        # com nome
docker run --rm -it ubuntu bash              # remove ao sair

# Listar
docker ps                                    # rodando
docker ps -a                                # todos
docker ps -q                                # só IDs

# Controle
docker stop meu-nginx
docker start meu-nginx
docker restart meu-nginx
docker rm meu-nginx
docker rm -f meu-nginx                      # força mesmo rodando
docker container prune                       # remove todos parados

# Executar comando em container rodando
docker exec meu-nginx ls /etc/nginx
docker exec -it meu-nginx bash              # shell interativo
docker exec -it meu-nginx sh                # Alpine (sem bash)

# Logs
docker logs meu-nginx
docker logs -f meu-nginx                    # follow
docker logs --tail 50 meu-nginx
docker logs --since 1h meu-nginx

# Copiar arquivos
docker cp arquivo.txt meu-nginx:/tmp/
docker cp meu-nginx:/etc/nginx/nginx.conf ./

# Monitorar
docker stats
docker stats meu-nginx
docker top meu-nginx                         # processos do container
docker inspect meu-nginx                     # tudo (JSON completo)

Flags do docker run

FlagDescrição
-dDetached — roda em background
-itInterativo com pseudo-TTY
--nameNome do container
--rmRemove ao parar
-p 8080:80Mapear porta host:container
-PMapear todas as EXPOSE para portas aleatórias
-e VAR=valorVariável de ambiente
--env-file .envArquivo de variáveis
-v /host:/containerBind mount
-v nome:/containerNamed volume
--network nomeRede específica
--restart unless-stoppedPolítica de restart
--memory 512mLimite de memória
--cpus 1.5Limite de CPU
-u 1000:1000Rodar como user:group
--read-onlyFilesystem read-only

Exemplo completo

bash
docker run -d \
  --name minha-app \
  -p 3000:3000 \
  -e NODE_ENV=production \
  -e DB_HOST=db \
  --env-file .env \
  -v $(pwd)/uploads:/app/uploads \
  --network minha-rede \
  --restart unless-stopped \
  --memory 512m \
  --cpus 1 \
  minha-app:latest

📄 Dockerfile

Instruções

FROM
Image base. Sempre com tag específica em produção.
RUN
Executa comando e cria nova layer. Combinar com && para reduzir layers.
COPY
Copia arquivos do contexto local para a image. Preferível ao ADD.
ADD
Como COPY, mas aceita URLs e descomprime tar automaticamente.
WORKDIR
Define diretório de trabalho. Cria se não existir.
ENV
Variável de ambiente — persiste no container em runtime.
ARG
Variável de build — passada com --build-arg. Não persiste no container.
EXPOSE
Documenta a porta que a app escuta. Não publica — use -p no run.
CMD
Comando padrão. Substituível em docker run <cmd>.
ENTRYPOINT
Executável fixo. CMD vira args padrão. docker run <cmd> adiciona args.
USER
Usuário para RUN/CMD/ENTRYPOINT. Nunca rodar como root.
HEALTHCHECK
Comando para verificar saúde do container. Usado pelo Compose no depends_on.
VOLUME
Declara ponto de montagem. Cria volume anônimo se não for montado.
LABEL
Metadata (maintainer, version, etc.). Inspecionável via docker inspect.

Exemplo completo — Node.js

Dockerfile
FROM node:20-alpine

LABEL maintainer="alfredo@email.com"

ARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV
ENV PORT=3000

WORKDIR /app

# Dependências ANTES do código — aproveita cache de layer
COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 3000

# Usuário não-root
RUN addgroup -g 1001 -S appgroup && \
    adduser -u 1001 -S appuser -G appgroup && \
    chown -R appuser:appgroup /app

USER appuser

HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
  CMD wget -qO- http://localhost:3000/health || exit 1

CMD ["node", "server.js"]

CMD vs ENTRYPOINT

CMD
✅ Substituível: docker run img <cmd>
Fornecer args padrão para ENTRYPOINT
Ou definir comando padrão da imagem
ENTRYPOINT
❌ Não substituível — apenas args mudam
Define o executável fixo do container
CMD vira os argumentos padrão
Dockerfile — padrão recomendado
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
# docker run img -t  →  nginx -t  (substitui CMD, não ENTRYPOINT)

Multi-stage build — imagem final sem ferramentas de build

Dockerfile
# Stage 1: build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: produção — só o necessário
FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist

RUN adduser -S appuser
USER appuser

CMD ["node", "dist/server.js"]

.dockerignore

.dockerignore
node_modules/
.git/
*.log
.env
.env.*
dist/
coverage/
.DS_Store
Dockerfile
docker-compose*.yml

Boas práticas

Tag específicanode:20.11-alpine3.19 não node:latest
COPY package.json antes do códigoAproveita cache — só reinstala se package.json mudar
Combinar RUN com &&Menos layers, imagem menor
Usuário não-rootUSER appuser antes do CMD
Multi-stage buildsImagem final sem compiladores, devDeps, código-fonte
.dockerignoreNunca incluir node_modules, .git, .env no contexto
Nunca ENV SECRET=valorSegredos em Dockerfile ficam visíveis em docker history
Nunca apt install sem limparSempre && rm -rf /var/lib/apt/lists/* no mesmo RUN

💾 Volumes

Dados dentro do container são efêmeros — perdidos ao remover. Volumes persistem fora do ciclo de vida do container.

Named Volume
-v nome:/caminho
Gerenciado pelo Docker. Ideal para banco de dados e dados que o Docker deve controlar.
Bind Mount
-v /host:/container
Monta diretório do host. Ideal para desenvolvimento (hot reload) e config files.
tmpfs
--tmpfs /caminho
Armazenamento em memória RAM. Dados não persistem nem mesmo entre restarts.
bash
# Named volume
docker volume create meus-dados
docker run -v meus-dados:/app/data nginx

# Bind mount
docker run -v $(pwd):/app node               # desenvolvimento
docker run -v /etc/nginx:/etc/nginx:ro nginx   # read-only

# tmpfs
docker run --tmpfs /tmp node

# Listar / inspecionar / remover
docker volume ls
docker volume inspect meus-dados
docker volume rm meus-dados
docker volume prune                            # remove não usados

🔀 Networks

bridge
Padrão. Containers no mesmo host se comunicam. Isolados da rede host. Network padrão não resolve nomes — use rede customizada.
bridge customizada
Recomendada. Containers se resolvem por nome (DNS interno). db chega no container chamado db.
host
Container usa a rede do host diretamente. Sem isolamento de rede. Porta do container = porta do host.
none
Sem rede. Isolamento total. Para containers que não precisam de conectividade.
bash
# Criar rede customizada
docker network create minha-rede

# Containers na mesma rede se comunicam por nome
docker run --network minha-rede --name app minha-app
docker run --network minha-rede --name db postgres
# dentro de `app`: psql -h db -U postgres

# Listar / inspecionar
docker network ls
docker network inspect minha-rede

# Conectar/desconectar container rodando
docker network connect minha-rede meu-container
docker network disconnect minha-rede meu-container

# Remover
docker network rm minha-rede
docker network prune

🌐 Registry

bash
# Login
docker login                          # Docker Hub
docker login ghcr.io                  # GitHub Container Registry
docker login registry.example.com     # registry privado

# Tag e push
docker tag minha-app:latest usuario/minha-app:1.0
docker push usuario/minha-app:1.0
docker push usuario/minha-app:latest

# Pull
docker pull usuario/minha-app:1.0

# Build + tag para push em uma etapa
docker build -t usuario/minha-app:1.0 .

# Build multi-arch (amd64 + arm64) com push direto
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t usuario/app:latest --push .

🐙 Docker Compose

Orquestra múltiplos containers com um único arquivo YAML. Define services, volumes e networks em conjunto. Ideal para desenvolvimento local e deploys simples.

📝

Declarativo

Um arquivo YAML descreve toda a stack. Versionar junto ao código.

Um comando

docker compose up -d sobe toda a stack. down derruba tudo.

🔗

DNS automático

Services se comunicam pelo nome. db chega no Postgres, redis no Redis.

ℹ️
docker compose (v2) vs docker-compose (v1)

O plugin moderno é docker compose (sem hífen), instalado junto ao Docker Engine. docker-compose é o binário legado standalone — deprecated. Use sempre o v2.

📋 docker-compose.yml

Chaves principais

services
Containers da stack. Cada service = um container (ou mais com replicas).
volumes
Named volumes compartilhados entre services ou persistidos.
networks
Redes compartilhadas. Por padrão cria uma rede para o projeto.
build
Context e Dockerfile para construir a image do service.
image
Image pré-existente do registry, ou nome da image que será gerada.
ports
Mapeamento host:container. Só expor o necessário.
environment
Variáveis de ambiente. Aceita lista ou mapa.
env_file
Arquivo .env com variáveis para o service.
depends_on
Ordem de startup e condição (started / healthy / completed).
healthcheck
Comando para verificar saúde. Usado pelo depends_on condition.
restart
no / always / on-failure / unless-stopped
profiles
Grupos opcionais ativados com --profile nome.

Exemplo completo — App + DB + Nginx

docker-compose.yml
name: minha-app

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        NODE_ENV: production
    image: minha-app:latest
    container_name: app
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DB_HOST=db
    env_file:
      - .env
    volumes:
      - ./uploads:/app/uploads
      - app-logs:/app/logs
    networks:
      - backend
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 512M

  db:
    image: postgres:16-alpine
    container_name: db
    restart: unless-stopped
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - backend
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U myuser -d mydb"]
      interval: 10s
      timeout: 5s
      retries: 5

  nginx:
    image: nginx:1.25-alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    networks:
      - backend
    depends_on:
      - app

volumes:
  postgres-data:
  app-logs:

networks:
  backend:
    driver: bridge

💻 Compose — Comandos

bash
# Subir
docker compose up
docker compose up -d                      # background
docker compose up --build                  # rebuild antes
docker compose up --build -d
docker compose up -d app                  # service específico

# Parar e remover
docker compose down
docker compose down -v                    # também remove volumes
docker compose down --rmi all             # também remove images

# Controle
docker compose stop / start / restart
docker compose restart app

# Status e logs
docker compose ps
docker compose ps -a
docker compose logs
docker compose logs -f
docker compose logs -f app
docker compose logs --tail 50 app

# Executar comandos
docker compose exec app bash
docker compose exec app npm run migrate
docker compose exec db psql -U myuser mydb

# Rodar container one-off (não usa depends_on)
docker compose run --rm app npm test
docker compose run --rm app node scripts/seed.js

# Build
docker compose build
docker compose build app
docker compose build --no-cache

# Config final (variáveis resolvidas)
docker compose config
docker compose config --quiet && echo "OK"

# Processos
docker compose top

# Arquivo alternativo
docker compose -f docker-compose.prod.yml up -d

🔑 Variáveis e .env

.env — lido automaticamente
APP_PORT=3000
DB_PASSWORD=secret
NODE_ENV=production
docker-compose.yml — usar variáveis
services:
  app:
    ports:
      - "${APP_PORT}:3000"
    environment:
      - NODE_ENV=${NODE_ENV}
      - DB_PASSWORD=${DB_PASSWORD}
bash — overrides
# Arquivo .env alternativo
docker compose --env-file .env.staging up

# Sobrescrever variável inline
APP_PORT=8080 docker compose up

🔀 Override Files

docker-compose.override.yml é aplicado automaticamente sobre o base. Ideal para separar config de desenvolvimento da produção.

docker-compose.override.yml — desenvolvimento
services:
  app:
    build:
      context: .
    volumes:
      - .:/app                # hot reload
      - /app/node_modules     # preserva node_modules do container
    command: npm run dev
    environment:
      - NODE_ENV=development
    ports:
      - "9229:9229"            # debug port

  db:
    ports:
      - "5432:5432"            # acesso direto pelo cliente local
bash
# Development: carrega base + override automaticamente
docker compose up -d

# Produção: só o base (sem override)
docker compose -f docker-compose.yml up -d

# Staging: combinar múltiplos
docker compose -f docker-compose.yml -f docker-compose.staging.yml up -d

🏷️ Profiles

Services opcionais ativados sob demanda. Evita subir ferramentas de debug em produção.

docker-compose.yml
services:
  app:
    image: minha-app              # sempre sobe

  db:
    image: postgres:16            # sempre sobe

  adminer:
    image: adminer
    profiles: [debug]             # só com --profile debug
    ports:
      - "8080:8080"

  seed:
    image: minha-app
    command: npm run seed
    profiles: [setup]
    depends_on:
      - db
bash
docker compose up -d                          # app + db
docker compose --profile debug up -d          # + adminer
docker compose --profile setup run seed       # roda seed

🔗 depends_on e ordem de startup

docker-compose.yml
services:
  app:
    depends_on:
      db:
        condition: service_healthy            # aguarda healthcheck OK
      redis:
        condition: service_started            # aguarda só subir
      migrations:
        condition: service_completed_successfully  # aguarda exit 0

  migrations:
    image: minha-app
    command: npm run migrate
    depends_on:
      db:
        condition: service_healthy
conditionQuando continua
service_startedContainer subiu (padrão)
service_healthyHealthcheck retorna sucesso
service_completed_successfullyContainer saiu com exit code 0

🔨 Build e Registry

bash
# Build básico
docker build -t minha-app .
docker build -t minha-app:1.0 .
docker build -t minha-app:latest -f Dockerfile.prod .

# Build args e sem cache
docker build --build-arg NODE_ENV=production -t minha-app .
docker build --no-cache -t minha-app .

# Stage específico (multi-stage)
docker build --target production -t minha-app:prod .
docker build --target builder -t minha-app:build .

# Build multi-arch (amd64 + arm64)
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t usuario/app:latest --push .

🔍 Debug e Diagnóstico

bash
# Shell dentro do container
docker exec -it container bash
docker exec -it container sh          # Alpine

# Container parou? Inspecionar sobrescrevendo entrypoint
docker run --rm -it --entrypoint sh minha-app

# Inspecionar tudo
docker inspect container
docker inspect container | jq '.[0].Config.Env'      # variáveis de ambiente
docker inspect container | jq '.[0].Mounts'          # volumes montados
docker inspect container | jq '.[0].NetworkSettings'  # rede

# Diferença do container vs image (arquivos modificados)
docker diff container

# Espaço em disco
docker system df
docker system df -v

# Eventos do daemon
docker events

# Compose
docker compose config          # config final resolvida
docker compose port app 3000   # porta mapeada
docker compose top             # processos por service

# Limpeza completa — remove tudo não usado
docker system prune
docker system prune -a --volumes   # inclusive volumes e todas images

🛡️ Segurança

Usuário não-rootUSER appuser no Dockerfile
Filesystem read-only--read-only + tmpfs para /tmp
Limitar recursos--memory + --cpus evita DoS acidental
Não expor portas desnecessáriasDB só na rede interna, sem ports no compose
Secrets, não ENV varsUsar secrets: no compose ou _FILE vars
Scanning de imagemdocker scout cves ou trivy image
Nunca ENV SECRET=valor em DockerfileVisível em docker history e layers da image
Nunca rodar como rootComprometimento do container = root no host

Secrets no Compose

docker-compose.yml
services:
  db:
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password  # lê do arquivo
    secrets:
      - db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt   # arquivo fora do repositório

Read-only com tmpfs

docker-compose.yml
services:
  app:
    read_only: true
    tmpfs:
      - /tmp
      - /app/tmp

📐 Padrões Comuns

Node.js com hot reload em desenvolvimento

docker-compose.override.yml
services:
  app:
    volumes:
      - .:/app
      - /app/node_modules     # evita sobrescrever node_modules do container
    command: npm run dev
    environment:
      - NODE_ENV=development
    ports:
      - "9229:9229"            # Node.js debugger

Worker + Redis

docker-compose.yml
services:
  app:
    build: .
    depends_on: [redis, db]

  worker:
    build: .
    command: npm run worker
    depends_on: [redis, db]
    deploy:
      replicas: 2             # escalar workers

  redis:
    image: redis:7-alpine
    volumes:
      - redis-data:/data

volumes:
  redis-data:

Migrations como service one-shot

docker-compose.yml
services:
  migrations:
    build: .
    command: npm run migrate
    depends_on:
      db:
        condition: service_healthy
    profiles: [setup]     # ou remover profiles para rodar sempre

  app:
    depends_on:
      migrations:
        condition: service_completed_successfully

⚡ Referência Rápida

Images
listar
docker images
baixar / buildar
docker pull nginx:alpine
docker build -t app:1.0 .
remover / limpar
docker rmi app:1.0
docker image prune -a
Containers
iniciar
docker run -d --name app -p 80:80 nginx
listar / logs / shell
docker ps -a
docker logs -f app
docker exec -it app bash
parar / remover
docker stop app && docker rm app
Compose — subir
subir tudo
docker compose up -d
docker compose up --build -d
subir service específico
docker compose up -d app
derrubar
docker compose down
docker compose down -v
Compose — operar
status / logs
docker compose ps
docker compose logs -f app
shell / comando
docker compose exec app bash
docker compose run --rm app npm test
rebuild
docker compose build --no-cache
Volumes e Networks
criar volume
docker volume create dados
docker volume ls / inspect / rm
criar rede
docker network create minha-rede
docker network ls / inspect / rm
Sistema
uso de disco
docker system df
limpar não usados
docker system prune
limpar tudo
docker system prune -a --volumes
stats
docker stats

Referências

📖

Docker Docs

docs.docker.com

📋

Compose file ref

docs.docker.com/compose/compose-file