DevTools

Cheatsheet Docker

Plataforma de contentores para criar, distribuir e executar aplicações

Voltar às linguagens
Docker
110 cards encontrados
Categorias:
Versões:

Imagens


10 cards
Listar imagens
docker images
docker image ls -a
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
docker images --filter "dangling=true"
docker images --filter "reference=nginx*"

docker images mostra imagens locais. --format personaliza colunas com templates Go. dangling=true filtra imagens sem tag (intermédias de build). reference filtra por nome.

Remover imagens
# Remover por nome:tag:
docker rmi nginx:latest

# Remover por ID:
docker rmi a1b2c3d4e5f6

# Forçar (mesmo com contentores parados):
docker rmi -f minha-app:old

# Remover todas sem tag (dangling):
docker image prune

# Remover TODAS as não utilizadas:
docker image prune -a

docker rmi remove imagens. -f força mesmo com contentores associados. docker image prune limpa dangling. prune -a remove todas sem contentores ativos — liberta muito espaço em CI/CD.

Search e Docker Hub
# Procurar imagens no Docker Hub:
docker search nginx
docker search --filter "is-official=true" python
docker search --filter "stars=100" redis

# Resultados: NAME, DESCRIPTION, STARS, OFFICIAL

# Imagens oficiais (curadas):
docker pull nginx         # oficial (sem prefixo)
docker pull bitnami/nginx # comunidade (com prefixo)

docker search pesquisa no Docker Hub via CLI. is-official=true filtra imagens curadas pela Docker. Imagens oficiais não têm prefixo. Comunidade usa user/imagem. Prefere sempre oficiais ou verificadas.

Pull de imagem
docker pull nginx:latest
docker pull node:20-alpine
docker pull postgres:16

# Tag específica:
docker pull python:3.12-slim

# Todas as tags (cuidado, pesado):
docker pull -a ubuntu

# Verificar:
docker images nginx

docker pull descarrega imagem do registry. Usa sempre tags específicas (node:20-alpine) em vez de latest. Tags alpine e slim são mais leves. -a descarrega todas as tags.

Inspect e history
# Detalhes completos (JSON):
docker inspect nginx:latest

# Campos específicos:
docker inspect --format="{{.Config.ExposedPorts}}" nginx

# Histórico de camadas:
docker history nginx:latest
docker history --no-trunc nginx    # comandos completos

# Tamanho por camada:
docker history --format "{{.Size}}\t{{.CreatedBy}}" nginx

docker inspect mostra configuração completa (env, ports, volumes). docker history lista camadas e comandos que as criaram. --no-trunc mostra comandos inteiros. Útil para debug e otimização de tamanho.

Imagens multi-arch (ARM/AMD)
# Ver plataformas suportadas:
docker manifest inspect nginx:latest

# Pull para arquitetura específica:
docker pull --platform linux/arm64 nginx:latest
docker pull --platform linux/amd64 node:20

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

Imagens multi-arch suportam AMD64 e ARM64 no mesmo tag. --platform força arquitetura. docker buildx cria imagens para múltiplas plataformas. Essencial para Apple Silicon (ARM) e servidores cloud (AMD64).

Build de imagem
# Build a partir do Dockerfile no diretório atual:
docker build -t minha-app:1.0 .

# Com ficheiro específico:
docker build -f Dockerfile.prod -t minha-app:prod .

# Com build args:
docker build --build-arg NODE_ENV=production -t app .

# Sem cache:
docker build --no-cache -t app .

# Verificar:
docker images minha-app

docker build -t cria imagem com nome e tag. O . é o build context (ficheiros acessíveis). -f especifica Dockerfile alternativo. --no-cache força rebuild completo. --build-arg passa variáveis.

Save e Load (transferir)
# Exportar imagem para ficheiro tar:
docker save -o nginx-backup.tar nginx:latest

# Comprimir:
docker save nginx:latest | gzip > nginx.tar.gz

# Importar noutro servidor:
docker load -i nginx-backup.tar
gunzip -c nginx.tar.gz | docker load

# Verificar:
docker images nginx

docker save exporta imagem como .tar (com todas as camadas). docker load importa. Ideal para transferir entre servidores sem registry. Comprime com gzip para reduzir tamanho.

Tag e renomear
# Criar nova tag para imagem existente:
docker tag minha-app:1.0 minha-app:latest
docker tag minha-app:1.0 registry.io/user/minha-app:1.0

# Formato: docker tag ORIGEM DESTINO

# A tag é apenas um ponteiro — não duplica dados
docker images    # mesmo IMAGE ID, tags diferentes

docker tag cria um alias para a mesma imagem (não duplica). Necessário antes de push para registries: o nome deve incluir o registry (registry.io/user/app). Múltiplas tags podem apontar para o mesmo ID.

Import e Export (contentor)
# Exportar filesystem de um contentor:
docker export -o app-fs.tar meu-contentor

# Importar como nova imagem:
docker import app-fs.tar minha-app:imported

# Diferença vs save/load:
# save  → imagem completa (camadas + metadata)
# export → só filesystem (sem histórico)
# import → imagem plana (1 camada)

docker export extrai o filesystem de um contentor (sem camadas). docker import cria imagem plana. Perde-se histórico e metadata. Usa save/load para imagens; export/import para snapshots rápidos.

Contentores


10 cards
Criar e executar (run)
# Básico:
docker run nginx

# Background + nome + porta:
docker run -d --name web -p 8080:80 nginx

# Interativo (shell):
docker run -it ubuntu:22.04 /bin/bash

# Com variáveis de ambiente:
docker run -d -e POSTGRES_PASSWORD=secret postgres:16

# Auto-remover ao parar:
docker run --rm alpine echo "olá"

docker run = create + start. -d corre em background. -it dá terminal interativo. -e define env vars. --rm remove ao parar. -p publica portas. É o comando mais usado.

Logs
# Ver logs:
docker logs web

# Follow (tempo real):
docker logs -f web

# Últimas 50 linhas:
docker logs --tail 50 web

# Com timestamps:
docker logs -t web

# Desde timestamp:
docker logs --since "2024-01-01T00:00:00" web

# Stderr apenas:
docker logs web 2>&1 | grep ERROR

docker logs mostra stdout/stderr do contentor. -f faz follow (como tail -f). --tail limita linhas. -t adiciona timestamps. --since filtra por data. Essencial para debug de aplicações.

Stats e Top
# Recursos em tempo real (todos):
docker stats

# Contentor específico:
docker stats web db

# Sem stream (snapshot):
docker stats --no-stream

# Processos dentro do contentor:
docker top web
docker top web -ef    # formato ps

# Uso de disco:
docker system df -v

docker stats mostra CPU, RAM, rede e I/O em tempo real. --no-stream dá um snapshot (útil em scripts). docker top lista processos internos. system df mostra espaço usado por imagens, contentores e volumes.

Listar contentores (ps)
# Só ativos:
docker ps

# Todos (incluindo parados):
docker ps -a

# Formato personalizado:
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

# Filtrar por estado:
docker ps --filter "status=exited"
docker ps --filter "ancestor=nginx"

# Último criado:
docker ps -l

docker ps lista contentores ativos. -a inclui parados. --format personaliza output. --filter filtra por estado, imagem, nome. -l mostra o último criado. Essencial para monitorização diária.

Inspect (detalhes)
# JSON completo:
docker inspect web

# Campos específicos (Go template):
docker inspect --format="{{.State.Status}}" web
docker inspect --format="{{.NetworkSettings.IPAddress}}" web
docker inspect --format="{{.Config.Image}}" web

# IP de rede:
docker inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" web

docker inspect retorna JSON com toda a configuração: estado, rede, mounts, env. --format extrai campos com templates Go. Mostra IP, portas, volumes, restart policy. Ferramenta essencial de troubleshooting.

Rename, Pause e Wait
# Renomear:
docker rename web antigo-web

# Pausar (congelar processos):
docker pause web
docker unpause web

# Esperar até parar (retorna exit code):
docker wait web

# Commit (contentor → imagem):
docker commit web minha-app:snapshot

rename muda o nome. pause congela processos (SIGSTOP) sem parar. wait bloqueia até o contentor terminar. commit cria imagem do estado atual — evita em produção; usa Dockerfile.

Start, Stop, Restart, Kill
docker start web         # iniciar contentor parado
docker stop web          # parar (SIGTERM, 10s, SIGKILL)
docker restart web       # stop + start
docker kill web          # SIGKILL imediato (forçar)

# Com timeout custom:
docker stop -t 30 web    # esperar 30s antes de kill

# Múltiplos:
docker stop web api db
docker start $(docker ps -aq)   # iniciar todos

stop envia SIGTERM e espera 10s antes de SIGKILL. kill é imediato (sem graceful shutdown). restart = stop + start. -t ajusta timeout. Aceita nomes ou IDs, múltiplos de uma vez.

Remover contentores
# Remover parado:
docker rm web

# Forçar (mesmo ativo):
docker rm -f web

# Remover ao parar (automático):
docker run --rm alpine echo "temporário"

# Limpar todos os parados:
docker container prune

# Remover todos (ativos e parados):
docker rm -f $(docker ps -aq)

docker rm remove contentores parados. -f força remoção de ativos (kill + rm). --rm no run remove automaticamente ao sair. container prune limpa todos os parados. Cuidado com $(docker ps -aq).

Exec (comandos no contentor)
# Executar comando em contentor ativo:
docker exec web nginx -t

# Shell interativo:
docker exec -it web /bin/sh
docker exec -it db psql -U postgres

# Como root:
docker exec -u root -it web /bin/sh

# Com variável de ambiente:
docker exec -e DEBUG=true web env

docker exec corre comandos dentro de um contentor em execução. -it para sessões interativas. -u define utilizador. Não reinicia o contentor. Ideal para debug, verificar configs e aceder a shells.

Copiar ficheiros (cp)
# Host → Contentor:
docker cp ./config.yml web:/etc/app/config.yml

# Contentor → Host:
docker cp web:/var/log/app.log ./logs/

# Diretório completo:
docker cp ./src web:/app/src

# Sintaxe: docker cp ORIGEM DESTINO
# Nome do contentor ou ID + caminho absoluto

docker cp copia ficheiros entre host e contentor. Funciona com contentores ativos ou parados. Sintaxe: contentor:/caminho. Não precisa de tar — copia diretamente. Útil para configs rápidas e extração de logs.

Redes


10 cards
Listar e criar redes
# Listar redes:
docker network ls

# Criar rede bridge:
docker network create minha-rede

# Criar com subnet custom:
docker network create --subnet 172.20.0.0/16 --gateway 172.20.0.1 app-net

# Detalhes:
docker network inspect minha-rede

docker network ls mostra redes (bridge, host, none por padrão). create cria rede bridge customizada. --subnet define range IP. Redes custom permitem DNS automático entre contentores.

Rede Host
# Contentor partilha rede do host (sem NAT):
docker run -d --network host nginx

# Não precisa de -p (usa portas do host diretamente)
# Nginx acessível em localhost:80

# Limitações:
# • Sem isolamento de rede
# • Não funciona no Docker Desktop (Mac/Win)
# • Ideal para performance máxima ou debug

--network host remove isolamento de rede — o contentor usa as interfaces do host diretamente. Sem NAT, sem -p. Melhor performance mas sem segurança. Só funciona em Linux. Útil para tools de rede e debugging.

Inspect de rede
docker network inspect app-net

# Campos úteis:
docker network inspect --format="{{range .Containers}}{{.Name}} {{end}}" app-net

# IP de um contentor:
docker inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" web

# Gateway e subnet:
docker network inspect --format="{{(index .IPAM.Config 0).Subnet}}" app-net

network inspect mostra subnet, gateway, contentores ligados e IPs. --format extrai dados específicos. Mostra driver, opções e labels. Essencial para debug de conectividade entre serviços.

Ligar contentor a rede
# Ao criar:
docker run -d --name api --network minha-rede minha-app

# Conectar contentor existente:
docker network connect minha-rede web

# Desconectar:
docker network disconnect minha-rede web

# Verificar:
docker network inspect minha-rede

--network no run liga à rede na criação. connect/disconnect gerem ligações em runtime. Um contentor pode estar em múltiplas redes. Na mesma rede, contentores comunicam por nome (DNS interno).

Rede None (isolamento)
# Sem rede (só loopback):
docker run --network none alpine ip addr

# Resultado: só interface lo (127.0.0.1)
# Sem acesso externo, sem DNS

# Casos de uso:
# • Processamento seguro (sem internet)
# • Testes de isolamento
# • Jobs que não precisam de rede

--network none remove toda a conectividade. O contentor fica totalmente isolado (só loopback). Ideal para processamento sensível, sandboxes e testes. Nenhuma porta funciona. Segurança máxima por isolamento.

IPv6 e redes Macvlan
# Rede com IPv6:
docker network create --ipv6 --subnet 2001:db8::/64 rede-v6

# Macvlan (contentor com IP na rede física):
docker network create -d macvlan \
  --subnet 192.168.1.0/24 \
  --gateway 192.168.1.1 \
  -o parent=eth0 macvlan-net

docker run -d --network macvlan-net --ip 192.168.1.200 meu-servico

--ipv6 ativa IPv6 na rede. macvlan dá um IP da rede física ao contentor (como uma máquina real). Sem NAT, sem port mapping. Ideal para servidores que precisam de IP direto na LAN. Requer interface física.

Publicar portas (-p)
# host:contentor
docker run -d -p 8080:80 nginx          # TCP
docker run -d -p 5353:53/udp dns-app    # UDP
docker run -d -p 127.0.0.1:3000:3000 app  # só localhost

# Múltiplas portas:
docker run -d -p 80:80 -p 443:443 web

# Porta aleatória no host:
docker run -d -p 80 nginx
docker port web    # ver mapeamento

-p host:contentor expõe portas. Sem IP, escuta em todas as interfaces. 127.0.0.1: restringe a localhost. /udp para UDP. docker port mostra mapeamentos. Sem -p, portas ficam internas.

Remover e limpar redes
# Remover rede (sem contentores ligados):
docker network rm minha-rede

# Remover todas as não utilizadas:
docker network prune

# Forçar (desconecta contentores):
docker network rm -f minha-rede

# Redes padrão (bridge, host, none) não podem ser removidas

network rm remove redes sem contentores. prune limpa todas as não usadas. Redes padrão (bridge, host, none) são permanentes. Desconecta contentores antes de remover ou usa -f.

DNS entre contentores
# Criar rede e contentores:
docker network create app-net
docker run -d --name db --network app-net postgres
docker run -d --name api --network app-net minha-api

# Dentro de "api", resolve "db" por nome:
docker exec api ping db       # funciona!
docker exec api getent hosts db  # IP do db

# DNS automático só em redes custom (não na bridge default)

Em redes bridge customizadas, o Docker ativa DNS interno. Contentores resolvem-se por nome (ex: ping db). Na rede default, DNS não funciona — precisa de --link (deprecated). Usa sempre redes custom.

Alias de rede
# Contentor com múltiplos nomes DNS:
docker run -d --name db1 --network app-net --network-alias database postgres

# Outros contentores resolvem ambos:
# ping db1     → funciona
# ping database → funciona (alias)

# Útil para service discovery:
docker run -d --name api1 --network app-net --network-alias api minha-app
docker run -d --name api2 --network app-net --network-alias api minha-app

--network-alias adiciona nomes DNS extras. Múltiplos contentores com o mesmo alias = round-robin DNS (load balancing simples). Ideal para service discovery sem ferramentas externas. Funciona em redes custom.

Docker Compose


10 cards
Estrutura do docker-compose.yml
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    depends_on:
      - api

  api:
    build: ./api
    environment:
      - DB_HOST=db
      - DB_PORT=5432
    depends_on:
      - db

  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: secret

volumes:
  pgdata:

Ficheiro YAML com secção services (contentores), volumes (dados) e networks (opcional). Cada serviço tem image ou build. depends_on define ordem de arranque. Compose cria rede automática.

Redes no Compose
services:
  frontend:
    image: nginx
    networks:
      - frontend-net

  api:
    build: ./api
    networks:
      - frontend-net
      - backend-net

  db:
    image: postgres:16
    networks:
      - backend-net

networks:
  frontend-net:
  backend-net:

Serviços comunicam só na mesma network. O exemplo isola: frontend fala com api, api fala com db, mas frontend não acede a db diretamente. Compose cria rede default automaticamente se não definir.

Compose Watch (hot-reload)
services:
  app:
    build: .
    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src
        - action: rebuild
          path: ./package.json

# Ativar:
# docker compose watch
# ou: docker compose up --watch -d

docker compose watch (Compose 2.22+) sincroniza alterações em tempo real. action: sync copia ficheiros sem rebuild. action: rebuild reconstrói imagem. Substitui bind mounts em muitos casos. Mais rápido e fiável.

Comandos essenciais
docker compose up -d          # criar e iniciar (background)
docker compose down           # parar e remover tudo
docker compose ps             # listar serviços
docker compose logs -f        # logs em tempo real
docker compose exec web sh    # shell num serviço
docker compose build          # rebuild imagens
docker compose pull           # pull de todas as imagens
docker compose restart api    # reiniciar serviço

up -d cria rede, volumes e inicia tudo. down remove contentores e rede (volumes ficam). logs -f segue todos os serviços. exec acede a shell. build reconstrói imagens com build local.

Perfis e overrides
# docker-compose.yml com profiles:
# services:
#   debug-tools:
#     profiles: ["debug"]
#     image: nicolaka/netshoot

# Iniciar só serviços base:
docker compose up -d

# Incluir perfil debug:
docker compose --profile debug up -d

# Override para desenvolvimento:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d

profiles ativam serviços condicionalmente. Sem --profile, serviços com perfil não iniciam. -f sobrepõe ficheiros (override). Padrão: base + dev/prod override. Segundo ficheiro sobrepõe o primeiro.

Logs e debugging
# Logs de todos os serviços:
docker compose logs -f

# Serviço específico:
docker compose logs -f api

# Últimas N linhas:
docker compose logs --tail 50 db

# Exec para debug:
docker compose exec api sh
docker compose exec db psql -U postgres

# Config validada:
docker compose config

compose logs -f agrega logs de todos os serviços. --tail limita linhas. exec abre shell num serviço. compose config valida e mostra o YAML resolvido (com overrides e .env aplicados).

Build com Compose
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.prod
      args:
        NODE_ENV: production
      target: production    # multi-stage
    ports:
      - "3000:3000"

  # Ou simples:
  worker:
    build: ./worker
SH
                ) . '<p><code>build</code> pode ser string (caminho) ou objeto com <code>context</code>, <code>dockerfile</code>, <code>args</code>. <code>target</code> seleciona etapa em multi-stage. <code>docker compose build</code> reconstrói. <code>up --build</code> faz build + up.</p>',
            ],
            [
                'title' => 'Variáveis e .env',
                'content' => $this->pre(
                    <<<'YAML'
# docker-compose.yml
services:
  app:
    image: minha-app:${TAG:-latest}
    environment:
      - DATABASE_URL=${DB_URL}
      - SECRET=${SECRET_KEY}

# .env (mesmo diretório, carregado automaticamente):
TAG=2.1.0
DB_URL=postgres://user:pass@db:5432/app
SECRET_KEY=super-secreto

O Compose carrega .env automaticamente para substituição de variáveis. ${VAR:-default} define fallback. environment passa vars ao contentor. Nunca commitar .env com segredos — usar .env.example.

Escalar serviços
# Escalar (múltiplas réplicas):
docker compose up -d --scale worker=3

# Ver réplicas:
docker compose ps

# Logs de todas as réplicas:
docker compose logs -f worker

# Limitar (sem porta fixa para escalar):
# ports: NÃO usar "8080:80" se escalar
# Usar range: "8080-8082:80" ou sem ports

--scale worker=3 cria 3 réplicas do serviço. Não funciona com ports fixos (conflito). Usa range de portas ou service discovery interno. Ideal para workers e processamento paralelo. Load balancing via DNS.

depends_on e healthcheck
services:
  app:
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

  redis:
    image: redis:7-alpine

depends_on com condition: service_healthy espera o healthcheck passar antes de iniciar dependentes. healthcheck define comando, intervalo e retries. service_started só espera iniciar (sem verificar saúde).

Restart policies
services:
  app:
    image: minha-app
    restart: unless-stopped

  db:
    image: postgres:16
    restart: always

  worker:
    image: worker-app
    restart: on-failure:3    # 3 tentativas

# Opções:
# no           → nunca reinicia (padrão)
# always       → sempre (mesmo manual stop)
# on-failure   → só se exit code != 0
# unless-stopped → sempre exceto stop manual

restart define política de reinício. unless-stopped é o mais usado (reinicia exceto se parado manualmente). on-failure:N limita tentativas. always reinicia mesmo após docker restart. Essencial em produção.

Dockerfile


10 cards
FROM e imagens base
# Imagem base oficial:
FROM node:20-alpine

# Com versão específica:
FROM python:3.12-slim

# Imagem mínima:
FROM alpine:3.19

# Scratch (vazia, para binários):
FROM scratch

# Escolha: alpine (5MB) < slim (30MB) < full (300MB+)
# Alpine usa musl libc (pode ter incompatibilidades)

FROM define a imagem base (primeira instrução). alpine é mínima (~5MB). slim remove tools desnecessários. scratch é vazia (para binários Go/Rust). Escolhe a menor que funcione para a tua app.

WORKDIR e EXPOSE
# Definir diretório de trabalho:
WORKDIR /app

# Tudo a seguir é relativo a /app:
COPY package.json .
RUN npm install
COPY . .

# Documentar porta (NÃO publica!):
EXPOSE 3000
EXPOSE 5432/tcp
EXPOSE 53/udp

# Publicar de facto: docker run -p 3000:3000

WORKDIR define o diretório (cria se não existir). Evita cd em RUN. EXPOSE é documentação — não publica porta. Publicação real é com -p no run ou ports no Compose. Sempre usa WORKDIR.

.dockerignore
# .dockerignore (na raiz do build context)
node_modules
.git
.env
*.md
dist/
coverage/
.vscode
docker-compose*.yml
Dockerfile*

# Reduz build context (mais rápido)
# Evita copiar segredos (.env)
# Semelhante ao .gitignore

.dockerignore exclui ficheiros do build context. Reduz tempo de build (menos dados enviados ao daemon). Evita copiar .env, node_modules, .git. Sintaxe igual ao .gitignore. Sempre criar um.

RUN (executar comandos)
# Instalar dependências:
RUN apt-get update && apt-get install -y \
    curl \
    git \
    && rm -rf /var/lib/apt/lists/*

# Múltiplos comandos numa camada:
RUN npm ci --production \
    && npm cache clean --force

# Alpine:
RUN apk add --no-cache python3 make g++

RUN executa comandos durante o build. Cada RUN cria uma camada — combina com && para minimizar. rm -rf /var/lib/apt/lists/* limpa cache. --no-cache no apk evita cache. Menos camadas = imagem menor.

CMD e ENTRYPOINT
# CMD: comando padrão (pode ser sobrescrito)
CMD ["node", "server.js"]

# ENTRYPOINT: binário fixo (args são passados)
ENTRYPOINT ["python", "app.py"]

# Combinar (entrypoint + args padrão):
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

# Sobrescrever CMD no run:
# docker run minha-app --debug

CMD define comando padrão (substituível no run). ENTRYPOINT é fixo — argumentos são anexados. Combinação: ENTRYPOINT como binário, CMD como args default. Formato JSON ["exec"] é preferido (sem shell wrapper).

LABEL e STOPSIGNAL
# Metadata:
LABEL maintainer="dev@empresa.com"
LABEL version="2.1.0"
LABEL description="API de gestão de clientes"

# Visível em: docker inspect --format="{{.Config.Labels}}"

# Sinal de paragem:
STOPSIGNAL SIGTERM

# Para apps que usam SIGQUIT:
STOPSIGNAL SIGQUIT

LABEL adiciona metadata (autor, versão, descrição). Visível via inspect. Útil para catalogar imagens. STOPSIGNAL define sinal de shutdown (padrão: SIGTERM). Nginx usa SIGQUIT para graceful stop.

COPY e ADD
# Copiar ficheiros (preferido):
COPY package*.json ./
COPY src/ ./src/
COPY config.yml /etc/app/

# ADD (extrai tar automaticamente):
ADD app.tar.gz /opt/app/

# De URL (ADD só):
ADD https://exemplo.com/file.zip /tmp/

# Com ownership:
COPY --chown=node:node . /app

COPY copia ficheiros locais (transparente, preferido). ADD adiciona features: extração de tar e download de URL. --chown define dono. Copia só o necessário — cada COPY invalida cache se o ficheiro mudar.

USER e permissões
# Criar user não-root:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Copiar com ownership:
COPY --chown=appuser:appgroup . /app

# Mudar para non-root:
USER appuser

# A partir daqui, tudo corre como appuser:
RUN whoami    # appuser
CMD ["node", "server.js"]

USER define o utilizador para comandos seguintes. Nunca correr como root em produção. Cria user com adduser -S (Alpine) ou useradd -r (Debian). --chown no COPY garante permissões corretas.

ENV e ARG
# ENV: variável em runtime (persiste no contentor)
ENV NODE_ENV=production
ENV APP_PORT=3000

# ARG: variável só durante build
ARG VERSION=1.0.0
ARG BUILD_DATE

# Usar ARG em RUN:
RUN echo "Building v${VERSION}"

# ARG não existe em runtime!
# Para passar ao runtime: ENV MY_VAR=$ARG_VAR

ENV define variáveis que existem no contentor em runtime. ARG só existe durante build (não persiste). Usa ARG para versões e configs de build. ENV para configs da aplicação. Ambos acessíveis via $VAR.

HEALTHCHECK
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

# Sem curl (Alpine):
HEALTHCHECK CMD wget -qO- http://localhost:80/ || exit 1

# Estados: starting → healthy / unhealthy
# Visível em: docker ps (STATUS column)

HEALTHCHECK define verificação de saúde. --interval frequência, --timeout limite, --retries falhas até unhealthy. --start-period dá tempo para arrancar. Docker e Compose usam para restart e depends_on.

Registry e Distribuição


10 cards
Docker Hub (push/pull)
# Login:
docker login

# Tag com username:
docker tag minha-app:1.0 meuuser/minha-app:1.0

# Push:
docker push meuuser/minha-app:1.0

# Pull noutro servidor:
docker pull meuuser/minha-app:1.0

# Logout:
docker logout

docker login autentica no Docker Hub. Tag deve incluir username/. push envia camadas. pull descarrega. Imagens públicas são grátis; privadas têm limite no plano free. Usa tokens em CI/CD.

Docker Content Trust
# Ativar assinatura de imagens:
export DOCKER_CONTENT_TRUST=1

# Push agora assina automaticamente:
docker push meuuser/app:1.0

# Pull verifica assinatura:
docker pull meuuser/app:1.0

# Desativar temporariamente:
docker pull --disable-content-trust meuuser/app:1.0

Docker Content Trust (DCT) assina e verifica imagens. Garante integridade e publisher. Ativa com DOCKER_CONTENT_TRUST=1. Push cria assinatura; pull verifica. Protege contra imagens adulteradas.

Scan de vulnerabilidades
# Docker Scout (sucessor do docker scan):
docker scout cves minha-app:1.0

# Resumo rápido:
docker scout quickview minha-app:1.0

# Comparar versões:
docker scout diff minha-app:1.0 minha-app:1.1

# Trivy (alternativa open-source):
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image minha-app:1.0

docker scout analisa CVEs na imagem. quickview dá resumo. diff compara entre versões. Trivy é alternativa open-source. Integra em CI/CD para bloquear imagens com vulnerabilidades críticas.

Registry privado
# Correr registry local:
docker run -d -p 5000:5000 --name registry registry:2

# Tag para registry local:
docker tag minha-app localhost:5000/minha-app:1.0

# Push:
docker push localhost:5000/minha-app:1.0

# Pull:
docker pull localhost:5000/minha-app:1.0

# Listar imagens:
curl http://localhost:5000/v2/_catalog

registry:2 é o registry oficial self-hosted. Corre na porta 5000. Tag inclui host:porta/. Sem auth por padrão (adicionar TLS + htpasswd em produção). API REST em /v2/ para gestão.

Imagens multi-registry
# Tag para múltiplos registries:
docker tag app:1.0 ghcr.io/user/app:1.0
docker tag app:1.0 registry.gitlab.com/user/app:1.0
docker tag app:1.0 123456.dkr.ecr.us-east-1.amazonaws.com/app:1.0

# Push para todos:
docker push ghcr.io/user/app:1.0
docker push registry.gitlab.com/user/app:1.0

# Mesmo IMAGE ID, registries diferentes

Uma imagem pode ter tags em múltiplos registries. Mesmo IMAGE ID, destinos diferentes. Útil para mirror (redundância) ou migração. Cada push envia só camadas que o registry não tem (deduplicação).

Manifest e attestation
# Ver manifest (plataformas, camadas):
docker manifest inspect nginx:latest

# Criar manifest list manual:
docker manifest create meuuser/app:1.0 \
  meuuser/app:1.0-amd64 \
  meuuser/app:1.0-arm64

docker manifest push meuuser/app:1.0

# Attestation (SBOM, provenance):
docker buildx build --sbom=true --provenance=true -t app .

manifest descreve plataformas e camadas de uma imagem. manifest create une imagens multi-arch. attestation adiciona SBOM e provenance ao build. Aumenta transparência e segurança na supply chain.

Login em registries
# Docker Hub:
docker login

# GitHub Container Registry:
echo $TOKEN | docker login ghcr.io -u USER --password-stdin

# AWS ECR:
aws ecr get-login-password | docker login --username AWS --password-stdin ID.dkr.ecr.REGION.amazonaws.com

# Google GCR:
gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin gcr.io

# Azure ACR:
az acr login --name meuregistry

Cada cloud tem método de login próprio. --password-stdin evita secrets no histórico. ghcr.io usa PAT. ECR usa token temporário da AWS CLI. Credenciais ficam em ~/.docker/config.json.

Registry com autenticação
# Criar password file:
docker run --rm --entrypoint htpasswd httpd:2 -Bbn user pass > auth/htpasswd

# Registry com auth + TLS:
docker run -d -p 5000:5000 \
  -v $(pwd)/auth:/auth \
  -e REGISTRY_AUTH=htpasswd \
  -e REGISTRY_AUTH_HTPASSWD_REALM="Registry" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  -v certs:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  registry:2

Registry em produção precisa de TLS + htpasswd. Cria ficheiro com htpasswd -Bbn. Configura via variáveis de ambiente. Sem TLS, Docker recusa push (insecure-registry só para dev).

Tags e versionamento
# Boas práticas de tags:
docker tag app minha-app:1.2.3       # semver
docker tag app minha-app:latest      # última
docker tag app minha-app:$(git rev-parse --short HEAD)  # git SHA

# Push múltiplas tags:
docker push meuuser/app:1.2.3
docker push meuuser/app:latest

# Listar tags (Docker Hub API):
curl "https://hub.docker.com/v2/repositories/meuuser/app/tags"

Versionamento: usa semver (1.2.3) + latest para a mais recente. Em CI/CD, tag com git SHA para rastreabilidade. Nunca usar só latest em produção — pode mudar inesperadamente.

Garbage collection
# Limpar blobs não referenciados no registry:
docker exec registry registry garbage-collect /etc/docker/registry/config.yml

# Parar registry primeiro (mais seguro):
docker stop registry
docker run --rm -v registry-data:/var/lib/registry registry:2 \
  garbage-collect /etc/docker/registry/config.yml
docker start registry

# Verificar espaço:
docker system df

garbage-collect remove blobs órfãos do registry (camadas sem tag). Acontece após delete de tags. Não liberta espaço imediatamente — precisa de GC. Corre com registry parado para consistência.

Sistema e Manutenção


10 cards
docker info e version
# Informação do daemon:
docker info

# Mostra: Containers, Images, Storage Driver,
# Cgroup, Kernel, OS, CPUs, Total Memory

# Versão (client + server):
docker version

# Só versão curta:
docker --version

docker info mostra estado completo: nº de contentores, imagens, storage driver, memória, CPUs. docker version mostra versão do client e server. Primeiro comando para diagnosticar problemas.

Eventos em tempo real
# Todos os eventos:
docker events

# Filtrar:
docker events --filter "type=container"
docker events --filter "event=start"
docker events --filter "image=nginx"

# Com formato:
docker events --format "{{.Time}} {{.Action}} {{.Actor.Attributes.name}}"

# Desde/até:
docker events --since "2024-01-01" --until "2024-01-02"

docker events stream de eventos do daemon: create, start, stop, die, pull. --filter por tipo, evento ou imagem. Útil para auditoria, alertas e automação. Mostra timestamp, ação e metadata.

Troubleshooting comum
# "Cannot connect to Docker daemon"
sudo systemctl start docker

# "permission denied"
sudo usermod -aG docker $USER && newgrp docker

# "port already in use"
sudo lsof -i :8080    # ver quem usa a porta

# "no space left on device"
docker system prune -af

# Contentor não arranca:
docker logs contentor
docker inspect --format="{{.State.Error}}" contentor

Erros comuns: daemon parado (systemctl start), permissões (usermod), porta ocupada (lsof), disco cheio (prune). Sempre verifica docker logs e inspect para detalhes do erro.

Uso de disco (system df)
# Resumo:
docker system df

# Detalhado:
docker system df -v

# Output:
# TYPE            TOTAL   ACTIVE   SIZE      RECLAIMABLE
# Images          15      3        12.5GB    10.2GB (81%)
# Containers      5       2        1.2GB     800MB (66%)
# Local Volumes   8       3        500MB     200MB (40%)
# Build Cache     30      0        3GB       3GB

docker system df mostra espaço por tipo. RECLAIMABLE indica o que pode ser limpo. -v lista cada item. Build Cache acumula muito em CI/CD. Verifica regularmente para evitar disco cheio.

Reiniciar daemon com segurança
# Sem live-restore: todos os contentores param!
sudo systemctl restart docker

# Com live-restore (daemon.json):
# { "live-restore": true }
# → contentores continuam ativos durante restart

# Verificar após restart:
docker ps
docker info

# Reload config sem restart:
sudo systemctl reload docker

restart docker para todos os contentores (sem live-restore). Ativa live-restore: true no daemon.json para manter contentores ativos. reload aplica config sem reiniciar. Sempre verifica docker ps após.

Atualizar sem downtime
# 1. Ativar live-restore no daemon.json:
# { "live-restore": true }
sudo systemctl reload docker

# 2. Atualizar pacotes:
sudo apt-get install docker-ce docker-ce-cli containerd.io

# 3. Reiniciar (contentores continuam):
sudo systemctl restart docker

# 4. Verificar:
docker ps    # todos ainda ativos?
docker info  # versão nova?

Com live-restore: true, contentores sobrevivem ao restart do daemon. Atualiza pacotes e reinicia — apps continuam ativas. Sem live-restore, há downtime. Em produção com Swarm/K8s, faz rolling update.

Prune (limpeza geral)
# Limpar tudo que não está em uso:
docker system prune

# Incluir volumes (CUIDADO - dados!):
docker system prune --volumes

# Incluir imagens sem contentores:
docker system prune -a

# Sem confirmação:
docker system prune -af

# Só build cache:
docker builder prune

docker system prune remove contentores parados, redes órfãs e imagens dangling. -a inclui imagens sem contentores. --volumes remove volumes (perigoso!). builder prune limpa cache de build. Corre periodicamente.

Limitar logs de contentores
# Global (daemon.json):
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

# Por contentor:
docker run -d --log-opt max-size=10m --log-opt max-file=3 app

# Ver tamanho dos logs:
sudo du -sh /var/lib/docker/containers/*/*-json.log

Logs podem encher o disco! Configura max-size e max-file no daemon.json (global) ou por contentor. json-file é o driver padrão. Alternativas: syslog, fluentd, local.

Logs do daemon
# Linux (systemd):
sudo journalctl -u docker.service -f
sudo journalctl -u docker.service --since "1 hour ago"

# Verificar erros:
sudo journalctl -u docker.service | grep -i error

# Docker Desktop (Mac/Win):
# Settings → Troubleshoot → View logs

# Logs de contentores:
docker logs --tail 100 -f nome-contentor

No Linux, logs do daemon via journalctl -u docker.service. Mostra erros de arranque, OOM, problemas de rede. No Docker Desktop, logs na UI. Para contentores: docker logs. Essencial para troubleshooting.

Storage drivers
# Ver driver atual:
docker info | grep "Storage Driver"

# Configurar (daemon.json):
{ "storage-driver": "overlay2" }

# Drivers disponíveis:
# overlay2  → recomendado (Linux 4.0+)
# fuse-overlayfs → rootless
# btrfs/zfs → avançados (snapshots nativos)

# Verificar suporte:
grep overlay /proc/filesystems

overlay2 é o storage driver recomendado. Usa camadas com copy-on-write. fuse-overlayfs para rootless. Nunca muda driver com dados existentes (perde tudo). Verifica suporte no kernel antes.

Avançado


10 cards
Multi-stage builds
# Etapa 1: Build
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Etapa 2: Produção (imagem mínima)
FROM nginx:alpine AS production
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Multi-stage usa múltiplos FROM. Copia só artefactos com --from=builder. Imagem final não tem node_modules, source, etc. Reduz de ~1GB para ~25MB. Padrão essencial para produção.

Init e signal handling
# Problema: PID 1 não gere zombies/sinais
# Solução: usar init:
docker run -d --init minha-app

# Ou no Dockerfile:
# ENTRYPOINT ["docker-init", "--", "node", "server.js"]

# Verificar sinais:
docker stop app    # envia SIGTERM
# App deve tratar SIGTERM para graceful shutdown

# Sem init: processos zombie acumulam

--init injeta tini como PID 1. Gere sinais e processos zombie corretamente. Sem init, o processo da app é PID 1 e pode ignorar SIGTERM. Sempre usa --init ou trata sinais na app.

Otimizar imagens
# 1. Base mínima:
FROM node:20-alpine

# 2. Ordem para cache (muda menos → primeiro):
COPY package*.json ./
RUN npm ci --production
COPY . .

# 3. Multi-stage (não levar source):
FROM alpine AS runtime
COPY --from=builder /app/dist ./dist

# 4. Combinar RUN:
RUN apk add --no-cache curl && rm -rf /var/cache/apk/*

# 5. .dockerignore (sem node_modules, .git)

Otimização: base alpine, multi-stage, ordem de COPY para cache, combinar RUN, .dockerignore. Copia package.json antes do source (cache de npm install). Objetivo: imagem menor, build mais rápido.

BuildKit e cache avançado
# syntax=docker/dockerfile:1

# Cache de npm entre builds:
RUN --mount=type=cache,target=/root/.npm \
    npm ci --production

# Cache de apt:
RUN --mount=type=cache,target=/var/cache/apt \
    apt-get update && apt-get install -y curl

# Segredo no build (sem ficar na imagem):
RUN --mount=type=secret,id=npm_token \
    npm ci --registry=https://$(cat /run/secrets/npm_token)@npm.pkg.github.com

BuildKit permite --mount=type=cache (cache persistente entre builds) e --mount=type=secret (segredos sem persistir na imagem). Builds paralelos e mais rápidos. Ativo por padrão em Docker 23+.

docker buildx
# Criar builder:
docker buildx create --name meu-builder --use

# Build multi-platform:
docker buildx build --platform linux/amd64,linux/arm64 \
  -t meuuser/app:1.0 --push .

# Build com output local:
docker buildx build --platform linux/arm64 -o type=docker -t app:arm .

# Inspecionar builder:
docker buildx inspect --bootstrap

# Remover:
docker buildx rm meu-builder

buildx estende o build: multi-platform, cache remoto, output custom. --platform define arquiteturas. --push envia direto ao registry. -o type=docker carrega localmente. Essencial para ARM + AMD.

Docker em CI/CD
# GitHub Actions (exemplo):
# - name: Build & Push
#   run: |
#     docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASS }}
#     docker build -t user/app:${{ github.sha }} .
#     docker push user/app:${{ github.sha }}

# Cache de camadas:
docker build --cache-from user/app:latest -t user/app:new .

# Testar antes de push:
docker run --rm user/app:new npm test

# Só push se testes passarem

Em CI/CD: build com tag do git SHA, testa, e só depois push. --cache-from usa imagem anterior como cache. Login com secrets (nunca hardcoded). --rm limpa contentor de teste. Padrão para deploys fiáveis.

Docker Swarm (básico)
# Iniciar swarm:
docker swarm init --advertise-addr 192.168.1.10

# Criar serviço:
docker service create --name web --replicas 3 -p 80:80 nginx

# Listar serviços:
docker service ls
docker service ps web    # tarefas

# Escalar:
docker service scale web=5

# Update rolling:
docker service update --image nginx:1.25 web

Swarm é orquestração nativa do Docker. swarm init cria cluster. service create com --replicas distribui em nós. scale ajusta réplicas. update faz rolling update. Mais simples que Kubernetes.

Entrypoint scripts
# entrypoint.sh
#!/bin/sh
set -e

# Esperar BD:
until pg_isready -h db -p 5432; do
  echo "À espera da BD..."
  sleep 2
done

# Run migrations:
npm run migrate

# Executar comando principal:
exec "$@"

Scripts de entrypoint preparam o ambiente antes da app. Espera dependências, corre migrations, configura env. exec "$@" substitui o shell pelo CMD (recebe sinais). set -e para em erros. Padrão em produção.

Healthcheck avançado
# No Compose com depends_on:
# app depende de db estar healthy

# Ver estado:
docker inspect --format="{{.State.Health.Status}}" app
docker inspect --format="{{range .State.Health.Log}}{{.Output}}{{end}}" app

# Estados: starting → healthy | unhealthy

# Sem healthcheck no Dockerfile (no run):
docker run -d --health-cmd="curl -f http://localhost/ || exit 1" \
  --health-interval=30s --health-retries=3 app

Health.Status mostra starting/healthy/unhealthy. Health.Log tem output das verificações. Compose usa para depends_on condition. Pode definir no Dockerfile ou no run. Essencial para auto-healing.

Docker API
# Via socket Unix:
curl --unix-socket /var/run/docker.sock http://localhost/v1.45/containers/json

# Listar contentores:
curl --unix-socket /var/run/docker.sock http://localhost/containers/json?all=true

# Criar contentor:
curl --unix-socket /var/run/docker.sock -X POST \
  -H "Content-Type: application/json" \
  -d '{"Image":"alpine","Cmd":["echo","olá"]}' \
  http://localhost/containers/create

# Info:
curl --unix-socket /var/run/docker.sock http://localhost/info

O Docker expõe API REST via /var/run/docker.sock. Tudo que o CLI faz, a API faz. Versão: /v1.45/. Usada por ferramentas (Portainer, Traefik). Cuidado: acesso ao socket = acesso root.

Instalação e Setup


10 cards
Instalar no Linux (Ubuntu/Debian)
# Remover versões antigas
sudo apt-get remove docker docker-engine docker.io

# Instalar dependências
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg

# Adicionar chave GPG oficial
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

# Adicionar repositório
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list

# Instalar Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Instala o Docker Engine no Ubuntu/Debian via repositório oficial. O docker-compose-plugin inclui o Compose V2. Usa sempre o repositório oficial para receber atualizações de segurança.

Primeiro contentor (hello-world)
docker run hello-world

# O que acontece:
# 1. Docker procura imagem localmente
# 2. Não encontra → faz pull do Docker Hub
# 3. Cria contentor a partir da imagem
# 4. Executa o binário → imprime mensagem
# 5. Contentor termina (exit 0)

docker ps -a    # ver contentor (estado Exited)

O hello-world demonstra o ciclo completo: pullcreaterunexit. O contentor para após executar. docker ps -a mostra-o como Exited. Base para entender o modelo de contentores.

Atualizar Docker
# Linux (Ubuntu/Debian):
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Verificar versão:
docker --version

# Windows/Mac:
# Docker Desktop → Settings → Software Updates
# Ou: winget upgrade Docker.DockerDesktop (Windows)

# Notas de versão:
# https://docs.docker.com/engine/release-notes/

No Linux, atualiza via apt-get (mesmo comando da instalação). No Windows/Mac, o Docker Desktop atualiza automaticamente ou via winget. Mantém atualizado para patches de segurança e novas features.

Instalar no Windows e Mac
# Windows / Mac: Docker Desktop
# Download: https://www.docker.com/products/docker-desktop/

# Windows: requer WSL2 (Windows Subsystem for Linux)
wsl --install              # ativar WSL2
# Depois instalar Docker Desktop (installer .exe)

# Mac (Apple Silicon ou Intel):
# Download .dmg → arrastar para Applications

# Verificar instalação:
docker --version
docker compose version

Docker Desktop é a forma recomendada para Windows e Mac. No Windows, usa WSL2 como backend (mais rápido que Hyper-V). No Mac, usa uma VM leve. Inclui Docker Engine, Compose e BuildKit.

docker run (primeiro servidor)
# Nginx em background, porta 8080 → 80
docker run -d --name web -p 8080:80 nginx

# Abrir no browser: http://localhost:8080

# Ver logs:
docker logs web

# Parar e remover:
docker stop web
docker rm web

docker run -d executa em background (detached). -p 8080:80 mapeia porta host→contentor. --name dá um nome amigável. O Nginx serve a página padrão em localhost:8080. Primeiro passo para servir aplicações.

Conceitos fundamentais
# Imagem    → template read-only (ex: nginx:alpine)
# Contentor → instância em execução de uma imagem
# Volume    → dados persistentes fora do contentor
# Rede      → comunicação entre contentores
# Registry  → repositório de imagens (Docker Hub)
# Daemon    → serviço Docker (dockerd)
# Client    → CLI que fala com o daemon (docker)

# Arquitetura: Client → Daemon → Registry
# docker run = pull + create + start

Imagem é o template; contentor é a instância viva. Volumes persistem dados. Redes ligam contentores. O daemon (dockerd) executa tudo. O client (docker CLI) envia comandos via API REST.

Verificar instalação
docker --version          # Docker version 27.x.x
docker compose version    # Docker Compose version v2.x.x
docker info               # detalhes do daemon
docker run hello-world    # teste completo

# Se "permission denied" no Linux:
sudo usermod -aG docker $USER
newgrp docker             # aplicar sem logout

docker --version confirma a instalação. docker run hello-world testa o fluxo completo (pull + create + run). No Linux, adiciona o utilizador ao grupo docker para evitar sudo.

Serviço Docker (systemd)
# Gerir o daemon como serviço:
sudo systemctl start docker
sudo systemctl stop docker
sudo systemctl restart docker
sudo systemctl status docker

# Arrancar automaticamente no boot:
sudo systemctl enable docker

# Verificar se está ativo:
systemctl is-active docker    # "active"

No Linux, o Docker corre como serviço systemd. enable garante arranque no boot. status mostra estado e PID. No Docker Desktop (Windows/Mac), o serviço é gerido pela aplicação automaticamente.

Configurar o daemon (daemon.json)
# /etc/docker/daemon.json (Linux)
# ou Settings → Docker Engine (Docker Desktop)
{
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" },
  "storage-driver": "overlay2",
  "live-restore": true,
  "default-address-pools": [
    { "base": "172.30.0.0/16", "size": 24 }
  ]
}

# Reiniciar após alterações:
sudo systemctl restart docker

O ficheiro daemon.json configura o Docker globalmente. log-opts limita logs (evita disco cheio). overlay2 é o storage driver recomendado. live-restore mantém contentores ativos ao reiniciar o daemon.

Docker Context (múltiplos hosts)
# Listar contextos:
docker context ls

# Criar contexto remoto:
docker context create producao --docker "host=ssh://user@192.168.1.100"

# Mudar de contexto:
docker context use producao

# Comandos agora correm no host remoto:
docker ps    # lista contentores do servidor remoto

# Voltar ao local:
docker context use default

Docker Context permite gerir múltiplos hosts sem alterar variáveis de ambiente. create define um endpoint (local ou SSH). use troca o alvo. Ideal para gerir servidores remotos com os mesmos comandos.

Volumes


10 cards
Criar e listar volumes
# Criar volume nomeado:
docker volume create dados-db

# Listar:
docker volume ls

# Filtrar:
docker volume ls --filter "dangling=true"

# Detalhes:
docker volume inspect dados-db
# Mostra: Mountpoint, Driver, Labels, Scope

docker volume create cria volume gerido pelo Docker. ls lista todos. dangling=true mostra não usados. Dados ficam em /var/lib/docker/volumes/. Sobrevivem à remoção de contentores.

Partilhar dados entre contentores
# Mesmo volume em múltiplos contentores:
docker volume create partilhado
docker run -d --name app1 -v partilhado:/data app
docker run -d --name app2 -v partilhado:/data app

# Ambos leem/escrevem em /data

# Volume de outro contentor (deprecated):
docker run -d --volumes-from app1 novo-app

Múltiplos contentores podem montar o mesmo volume. Leituras e escritas são partilhadas. Cuidado com concorrência (sem locking). --volumes-from herda mounts de outro contentor (legado). Prefere volumes nomeados.

Volumes vs Bind Mounts
# Volume (gerido pelo Docker):
docker run -v dados:/var/lib/mysql db
# + Portável, backup fácil, drivers
# + Funciona igual em Linux/Mac/Win

# Bind mount (pasta do host):
docker run -v $(pwd)/src:/app/src app
# + Hot-reload em desenvolvimento
# - Depende da estrutura do host
# - Permissões podem conflitar

Volumes são geridos pelo Docker (portáveis, backup, drivers). Bind mounts ligam pasta do host (hot-reload, debug). Em produção: volumes. Em desenvolvimento: bind mounts para código. Nunca bind mount para dados de BD.

Montar volume no run
# Volume nomeado:
docker run -d -v dados-db:/var/lib/postgresql/data postgres

# Sintaxe: -v nome_volume:/caminho/contentor

# Se volume não existe, Docker cria automaticamente:
docker run -d -v novo-vol:/data alpine

# Com driver específico:
docker run -d --mount type=volume,dst=/data,volume-driver=local app

-v volume:/caminho monta volume nomeado. Se não existir, é criado automaticamente. Dados persistem entre restarts e remoções. --mount é a sintaxe explícita (preferida em produção). Caminho no contentor é absoluto.

Backup de volume
# Backup: volume → tar no host
docker run --rm -v dados-db:/fonte -v $(pwd):/backup alpine \
  tar czf /backup/backup-db.tar.gz -C /fonte .

# Restore: tar → volume
docker run --rm -v dados-db:/destino -v $(pwd):/backup alpine \
  tar xzf /backup/backup-db.tar.gz -C /destino

# Verificar:
docker run --rm -v dados-db:/data alpine ls /data

Backup usa contentor temporário com dois mounts: volume fonte + pasta destino. tar czf comprime. Restore inverte: tar do host → volume. --rm remove contentor após operação. Padrão essencial para dados críticos.

Permissões e ownership
# Contentor corre como UID 1000:
docker run -d --user 1000:1000 -v dados:/data app

# Fix permissões no Dockerfile:
# RUN chown -R node:node /app/data

# Verificar dono dos ficheiros:
docker exec app ls -la /data

# Problema comum: ficheiros criados como root
# Solução: --user ou chown no entrypoint

Problema comum: ficheiros criados como root dentro do contentor. Solução: --user UID:GID no run. Ou chown no Dockerfile. Bind mounts herdam permissões do host. Volumes novos são root por padrão.

Bind mount (pasta do host)
# Bind mount: pasta local → contentor
docker run -d -v $(pwd)/src:/app/src node:20

# Windows:
docker run -d -v C:\projeto\src:/app/src node:20

# Read-only:
docker run -d -v ./config:/etc/app:ro minha-app

# Sintaxe longa (recomendada):
docker run -d --mount type=bind,source="$(pwd)/src",target=/app/src app

Bind mount mapeia pasta do host para o contentor. Alterações são bidirecionais e imediatas. :ro torna read-only. Ideal para desenvolvimento (hot-reload). --mount type=bind é mais explícito e seguro.

tmpfs mount
# Montar em RAM (não persiste):
docker run -d --tmpfs /tmp:rw,size=100m minha-app

# Sintaxe longa:
docker run -d --mount type=tmpfs,destination=/cache,tmpfs-size=52428800 app

# Casos de uso:
# • Cache temporário rápido
# • Dados sensíveis (nunca toca disco)
# • Ficheiros de sessão

tmpfs monta em RAM — ultra-rápido mas volátil. Dados desaparecem ao parar o contentor. tmpfs-size limita tamanho em bytes. Ideal para cache, sessões e dados sensíveis que não devem tocar disco.

Remover e limpar volumes
# Remover volume:
docker volume rm dados-db

# Remover volumes de contentor ao apagar:
docker rm -v meu-contentor

# Limpar todos os não utilizados:
docker volume prune

# Remover volume específico de um contentor:
docker rm -v --force contentor-com-dados

volume rm remove volume (dados perdidos!). docker rm -v remove volumes anónimos do contentor. prune limpa dangling. Volumes nomeados NÃO são removidos com rm -v — só com volume rm explícito.

Volume drivers
# Driver local (padrão):
docker volume create --driver local meus-dados

# Com opções (NFS, por exemplo):
docker volume create --driver local \
  --opt type=nfs \
  --opt o=addr=192.168.1.50,rw \
  --opt device=:/exports/dados \
  nfs-vol

# Usar:
docker run -d -v nfs-vol:/data minha-app

O driver local é o padrão. Suporta opções como NFS, tmpfs. Drivers de terceiros: Azure File, AWS EBS, Ceph. --opt passa configurações do driver. Permite volumes em rede partilhados entre hosts.

Segurança e Recursos


10 cards
Limitar memória
# Limite de RAM:
docker run -d --memory 512m minha-app

# Com swap:
docker run -d --memory 512m --memory-swap 1g app

# Reserva mínima:
docker run -d --memory-reservation 256m app

# Ver uso:
docker stats minha-app

# OOM Kill (sem limite): kernel mata processo

--memory limita RAM máxima. --memory-swap limita RAM+swap. --memory-reservation é soft limit. Sem limite, um contentor pode consumir toda a RAM. Em produção, sempre define limites.

Capabilities (Linux)
# Remover todas e adicionar só necessárias:
docker run -d --cap-drop ALL --cap-add NET_BIND_SERVICE app

# Ver capabilities atuais:
docker inspect --format="{{.HostConfig.CapAdd}}" app
docker inspect --format="{{.HostConfig.CapDrop}}" app

# Perigoso: dar todas (equivalente a root):
docker run -d --privileged app    # EVITAR!

--cap-drop ALL remove privilégios Linux. --cap-add adiciona só o necessário (ex: NET_BIND_SERVICE para porta 80). --privileged dá acesso total — nunca em produção. Princípio do menor privilégio.

Isolamento de rede
# Sem rede externa (só interna):
docker network create --internal rede-isolada

docker run -d --network rede-isolada db

# Sem acesso a internet:
docker run -d --network none app

# DNS custom:
docker run -d --dns 8.8.8.8 --dns-search empresa.local app

# Hostname custom:
docker run -d --hostname meu-servidor app

--internal cria rede sem acesso externo (sem gateway). --network none isola totalmente. --dns define servidor DNS. --hostname personaliza nome interno. Combina para segurança em camadas.

Limitar CPU
# Limitar a 1.5 CPUs:
docker run -d --cpus 1.5 minha-app

# Percentagem de 1 CPU:
docker run -d --cpus 0.5 app    # 50% de 1 core

# CPUs específicos:
docker run -d --cpuset-cpus "0,1" app

# Peso relativo (shares):
docker run -d --cpu-shares 512 app   # padrão: 1024

--cpus limita CPUs (aceita decimais). --cpuset-cpus fixa a cores específicos. --cpu-shares define prioridade relativa. Evita que um contentor monopolize CPU. Monitoriza com docker stats.

Secrets (sem env vars)
# Mau: secrets em env (visível em inspect):
docker run -e DB_PASS=secret app

# Bom: montar como ficheiro:
echo "secret" > /tmp/db_pass.txt
docker run -d --mount type=bind,source=/tmp/db_pass.txt,target=/run/secrets/db_pass,readonly app

# Docker Swarm secrets:
docker secret create db_pass ./db_pass.txt
docker service create --secret db_pass minha-app

# Compose secrets:
# secrets:
#   db_pass:
#     file: ./db_pass.txt

Evita secrets em ENV (visíveis em inspect e logs). Monta como ficheiro read-only em /run/secrets/. Em Swarm, usa docker secret (encriptado em trânsito e repouso). Em Compose, secção secrets.

PID e IPC isolation
# Isolar PID namespace (padrão):
docker run -d minha-app    # já isolado

# Partilhar PID do host (debug):
docker run -d --pid host diagnostic-tool

# IPC isolation:
docker run -d --ipc private minha-app

# Partilhar memória (apps que usam shared memory):
docker run -d --ipc host minha-app

# UTS (hostname isolado por padrão):
docker run -d --hostname app1 minha-app

Docker isola PID, IPC, UTS, mount e network por padrão (namespaces). --pid host remove isolamento (só debug). --ipc host para apps com shared memory. Mantém isolamento em produção.

Utilizador non-root
# No Dockerfile:
# RUN adduser -D appuser
# USER appuser

# No run (override):
docker run -d --user 1000:1000 minha-app

# Verificar user atual:
docker exec app whoami
docker exec app id

# Ver user configurado na imagem:
docker inspect --format="{{.Config.User}}" minha-app

Correr como root é risco de segurança. Define USER no Dockerfile ou --user no run. Se um exploit acontecer, o atacante fica limitado ao user. Sempre usa non-root em produção.

Rootless Docker
# Instalar rootless:
curl -fsSL https://get.docker.com/rootless | sh

# Iniciar daemon:
export DOCKER_HOST=unix:///run/user/1000/docker.sock
dockerd-rootless.sh &

# Verificar:
docker info | grep -i rootless

# Vantagens:
# • Daemon não corre como root
# • Exploit não dá acesso ao host

Rootless Docker corre o daemon como user normal (sem root). Mesmo com exploit, atacante não ganha root no host. Usa user namespaces. Limitações: sem portas <1024, sem algumas redes. Recomendado para segurança.

Read-only filesystem
# Sistema de ficheiros read-only:
docker run -d --read-only minha-app

# Com tmpfs para escrita temporária:
docker run -d --read-only --tmpfs /tmp --tmpfs /var/run app

# Volume para dados persistentes:
docker run -d --read-only -v dados:/data app

# Verificar:
docker exec app touch /teste    # "Read-only file system"

--read-only torna o filesystem imutável. Previne malware de modificar ficheiros. Usa --tmpfs para dirs que precisam de escrita (/tmp, /var/run). Combina com volumes para dados. Segurança máxima.

Security options
# No-new-privileges (impede escalation):
docker run -d --security-opt no-new-privileges:true app

# AppArmor profile:
docker run -d --security-opt apparmor=meu-perfil app

# SELinux label:
docker run -d --security-opt label:type:svirt_custom_t app

# Seccomp profile custom:
docker run -d --security-opt seccomp=perfil.json app

no-new-privileges impede setuid/escalation. AppArmor/SELinux aplicam MAC (mandatory access control). seccomp filtra syscalls. Docker já aplica perfis padrão — personaliza para apps sensíveis.