DevTools

Cheatsheet Docker

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

Volver a los lenguajes
Docker
110 tarjetas encontradas
Categorías:
Versiones:

Imagens


10 cards
Listar imágenes
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 muestra las imágenes locales. --format personaliza las columnas con plantillas Go. dangling=true filtra imágenes sin tag (intermedias del build). reference filtra por nombre.

Eliminar imágenes
# Eliminar por nombre:tag:
docker rmi nginx:latest

# Eliminar por ID:
docker rmi a1b2c3d4e5f6

# Forzar (incluso con contenedores parados):
docker rmi -f my-app:old

# Eliminar todas sin tag (dangling):
docker image prune

# Eliminar TODAS las no utilizadas:
docker image prune -a

docker rmi elimina imágenes. -f fuerza incluso con contenedores asociados. docker image prune limpia las dangling. prune -a elimina todas las que no tienen contenedores activos — libera mucho espacio en CI/CD.

Search y Docker Hub
# Buscar imágenes en Docker Hub:
docker search nginx
docker search --filter "is-official=true" python
docker search --filter "stars=100" redis

# Resultados: NAME, DESCRIPTION, STARS, OFFICIAL

# Imágenes oficiales (curadas):
docker pull nginx         # oficial (sin prefijo)
docker pull bitnami/nginx # comunidad (con prefijo)

docker search búsqueda en Docker Hub vía CLI. is-official=true filtra imágenes curadas por Docker. Las imágenes oficiales no tienen prefijo. La comunidad usa user/imagen. Prefiere siempre oficiales o verificadas.

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

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

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

# Verificar:
docker images nginx

docker pull descarga una imagen del registry. Usa siempre tags específicas (node:20-alpine) en vez de latest. Las tags alpine y slim son más ligeras. -a descarga todas las tags.

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

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

# Historial de capas:
docker history nginx:latest
docker history --no-trunc nginx    # comandos completos

# Tamaño por capa:
docker history --format "{{.Size}}\t{{.CreatedBy}}" nginx

docker inspect muestra la configuración completa (env, ports, volumes). docker history lista las capas y los comandos que las crearon. --no-trunc muestra los comandos enteros. Útil para debug y optimización de tamaño.

Imágenes multi-arch (ARM/AMD)
# Ver las plataformas soportadas:
docker manifest inspect nginx:latest

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

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

Las imágenes multi-arch soportan AMD64 y ARM64 en el mismo tag. --platform fuerza la arquitectura. docker buildx crea imágenes para múltiples plataformas. Esencial para Apple Silicon (ARM) y servidores cloud (AMD64).

Build de imagen
# Build a partir del Dockerfile en el directorio actual:
docker build -t my-app:1.0 .

# Con un archivo específico:
docker build -f Dockerfile.prod -t my-app:prod .

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

# Sin caché:
docker build --no-cache -t app .

# Verificar:
docker images my-app

docker build -t crea una imagen con nombre y tag. El . es el build context (archivos accesibles). -f especifica un Dockerfile alternativo. --no-cache fuerza un rebuild completo. --build-arg pasa variables.

Save y Load (transferir)
# Exportar una imagen a un archivo tar:
docker save -o nginx-backup.tar nginx:latest

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

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

# Verificar:
docker images nginx

docker save exporta una imagen como .tar (con todas las capas). docker load la importa. Ideal para transferir entre servidores sin registry. Comprime con gzip para reducir el tamaño.

Tag y renombrar
# Crear una nueva tag para una imagen existente:
docker tag my-app:1.0 my-app:latest
docker tag my-app:1.0 registry.io/user/my-app:1.0

# Formato: docker tag ORIGEN DESTINO

# La tag es solo un puntero — no duplica datos
docker images    # mismo IMAGE ID, tags diferentes

docker tag crea un alias para la misma imagen (no duplica). Necesario antes del push a registries: el nombre debe incluir el registry (registry.io/user/app). Varias tags pueden apuntar al mismo ID.

Import y Export (contenedor)
# Exportar el filesystem de un contenedor:
docker export -o app-fs.tar my-container

# Importar como nueva imagen:
docker import app-fs.tar my-app:imported

# Diferencia vs save/load:
# save  → imagen completa (capas + metadata)
# export → solo filesystem (sin historial)
# import → imagen plana (1 capa)

docker export extrae el filesystem de un contenedor (sin capas). docker import crea una imagen plana. Se pierde el historial y la metadata. Usa save/load para imágenes; export/import para snapshots rápidos.

Contentores


10 cards
Crear y ejecutar (run)
# Básico:
docker run nginx

# Segundo plano + nombre + puerto:
docker run -d --name web -p 8080:80 nginx

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

# Con variables de entorno:
docker run -d -e POSTGRES_PASSWORD=secret postgres:16

# Auto-eliminar al parar:
docker run --rm alpine echo "hola"

docker run = create + start. -d corre en segundo plano. -it da una terminal interactiva. -e define env vars. --rm elimina al parar. -p publica puertos. Es el comando más usado.

Logs
# Ver los logs:
docker logs web

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

# Últimas 50 líneas:
docker logs --tail 50 web

# Con timestamps:
docker logs -t web

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

# Solo stderr:
docker logs web 2>&1 | grep ERROR

docker logs muestra el stdout/stderr del contenedor. -f hace follow (como tail -f). --tail limita las líneas. -t añade timestamps. --since filtra por fecha. Esencial para el debug de aplicaciones.

Stats y Top
# Recursos en tiempo real (todos):
docker stats

# Contenedor específico:
docker stats web db

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

# Procesos dentro del contenedor:
docker top web
docker top web -ef    # formato ps

# Uso de disco:
docker system df -v

docker stats muestra CPU, RAM, red e I/O en tiempo real. --no-stream da un snapshot (útil en scripts). docker top lista los procesos internos. system df muestra el espacio usado por imágenes, contenedores y volúmenes.

Listar contenedores (ps)
# Solo activos:
docker ps

# Todos (incluyendo 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 creado:
docker ps -l

docker ps lista los contenedores activos. -a incluye los parados. --format personaliza la salida. --filter filtra por estado, imagen, nombre. -l muestra el último creado. Esencial para la monitorización diaria.

Inspect (detalles)
# JSON completo:
docker inspect web

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

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

docker inspect devuelve JSON con toda la configuración: estado, red, mounts, env. --format extrae campos con plantillas Go. Muestra IP, puertos, volúmenes, restart policy. Herramienta esencial de troubleshooting.

Rename, Pause y Wait
# Renombrar:
docker rename web old-web

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

# Esperar hasta que pare (devuelve exit code):
docker wait web

# Commit (contenedor → imagen):
docker commit web my-app:snapshot

rename cambia el nombre. pause congela los procesos (SIGSTOP) sin parar. wait bloquea hasta que el contenedor termine. commit crea una imagen del estado actual — evítalo en producción; usa un Dockerfile.

Start, Stop, Restart, Kill
docker start web         # iniciar un contenedor parado
docker stop web          # parar (SIGTERM, 10s, SIGKILL)
docker restart web       # stop + start
docker kill web          # SIGKILL inmediato (forzar)

# Con timeout personalizado:
docker stop -t 30 web    # esperar 30s antes del kill

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

stop envía SIGTERM y espera 10s antes de SIGKILL. kill es inmediato (sin graceful shutdown). restart = stop + start. -t ajusta el timeout. Acepta nombres o IDs, varios a la vez.

Eliminar contenedores
# Eliminar uno parado:
docker rm web

# Forzar (incluso activo):
docker rm -f web

# Eliminar al parar (automático):
docker run --rm alpine echo "temporal"

# Limpiar todos los parados:
docker container prune

# Eliminar todos (activos y parados):
docker rm -f $(docker ps -aq)

docker rm elimina contenedores parados. -f fuerza la eliminación de activos (kill + rm). --rm en el run elimina automáticamente al salir. container prune limpia todos los parados. Cuidado con $(docker ps -aq).

Exec (comandos en el contenedor)
# Ejecutar un comando en un contenedor activo:
docker exec web nginx -t

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

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

# Con una variable de entorno:
docker exec -e DEBUG=true web env

docker exec ejecuta comandos dentro de un contenedor en ejecución. -it para sesiones interactivas. -u define el usuario. No reinicia el contenedor. Ideal para debug, verificar configs y acceder a shells.

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

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

# Directorio completo:
docker cp ./src web:/app/src

# Sintaxis: docker cp ORIGEN DESTINO
# Nombre del contenedor o ID + ruta absoluta

docker cp copia archivos entre el host y el contenedor. Funciona con contenedores activos o parados. Sintaxis: contenedor:/ruta. No necesita tar — copia directamente. Útil para configs rápidas y extracción de logs.

Redes


10 cards
Listar y crear redes
# Listar redes:
docker network ls

# Crear una red bridge:
docker network create my-network

# Crear con subnet personalizada:
docker network create --subnet 172.20.0.0/16 --gateway 172.20.0.1 app-net

# Detalles:
docker network inspect my-network

docker network ls muestra las redes (bridge, host, none por defecto). create crea una red bridge personalizada. --subnet define el rango IP. Las redes personalizadas permiten DNS automático entre contenedores.

Red Host
# El contenedor comparte la red del host (sin NAT):
docker run -d --network host nginx

# No necesita -p (usa los puertos del host directamente)
# Nginx accesible en localhost:80

# Limitaciones:
# • Sin aislamiento de red
# • No funciona en Docker Desktop (Mac/Win)
# • Ideal para máximo rendimiento o debug

--network host elimina el aislamiento de red — el contenedor usa las interfaces del host directamente. Sin NAT, sin -p. Mejor rendimiento pero sin seguridad. Solo funciona en Linux. Útil para herramientas de red y debugging.

Inspect de red
docker network inspect app-net

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

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

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

network inspect muestra subnet, gateway, contenedores conectados e IPs. --format extrae datos específicos. Muestra driver, opciones y labels. Esencial para el debug de conectividad entre servicios.

Conectar un contenedor a una red
# Al crear:
docker run -d --name api --network my-network my-app

# Conectar un contenedor existente:
docker network connect my-network web

# Desconectar:
docker network disconnect my-network web

# Verificar:
docker network inspect my-network

--network en el run conecta a la red en la creación. connect/disconnect gestionan conexiones en runtime. Un contenedor puede estar en varias redes. En la misma red, los contenedores se comunican por nombre (DNS interno).

Red None (aislamiento)
# Sin red (solo loopback):
docker run --network none alpine ip addr

# Resultado: solo la interfaz lo (127.0.0.1)
# Sin acceso externo, sin DNS

# Casos de uso:
# • Procesamiento seguro (sin internet)
# • Pruebas de aislamiento
# • Jobs que no necesitan red

--network none elimina toda la conectividad. El contenedor queda totalmente aislado (solo loopback). Ideal para procesamiento sensible, sandboxes y pruebas. Ningún puerto funciona. Máxima seguridad por aislamiento.

IPv6 y redes Macvlan
# Red con IPv6:
docker network create --ipv6 --subnet 2001:db8::/64 net-v6

# Macvlan (contenedor con IP en la red 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 my-service

--ipv6 activa IPv6 en la red. macvlan da al contenedor una IP de la red física (como una máquina real). Sin NAT, sin port mapping. Ideal para servidores que necesitan IP directa en la LAN. Requiere interfaz física.

Publicar puertos (-p)
# host:contenedor
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  # solo localhost

# Varios puertos:
docker run -d -p 80:80 -p 443:443 web

# Puerto aleatorio en el host:
docker run -d -p 80 nginx
docker port web    # ver el mapeo

-p host:contenedor expone puertos. Sin IP, escucha en todas las interfaces. 127.0.0.1: restringe a localhost. /udp para UDP. docker port muestra los mapeos. Sin -p, los puertos quedan internos.

Eliminar y limpiar redes
# Eliminar una red (sin contenedores conectados):
docker network rm my-network

# Eliminar todas las no utilizadas:
docker network prune

# Forzar (desconecta contenedores):
docker network rm -f my-network

# Las redes por defecto (bridge, host, none) no pueden eliminarse

network rm elimina redes sin contenedores. prune limpia todas las no usadas. Las redes por defecto (bridge, host, none) son permanentes. Desconecta los contenedores antes de eliminar o usa -f.

DNS entre contenedores
# Crear la red y los contenedores:
docker network create app-net
docker run -d --name db --network app-net postgres
docker run -d --name api --network app-net my-api

# Dentro de "api", resuelve "db" por nombre:
docker exec api ping db       # ¡funciona!
docker exec api getent hosts db  # IP de db

# DNS automático solo en redes personalizadas (no en la bridge por defecto)

En redes bridge personalizadas, Docker activa el DNS interno. Los contenedores se resuelven por nombre (ej: ping db). En la red por defecto, el DNS no funciona — necesita --link (deprecated). Usa siempre redes personalizadas.

Alias de red
# Contenedor con múltiples nombres DNS:
docker run -d --name db1 --network app-net --network-alias database postgres

# Otros contenedores resuelven ambos:
# ping db1     → funciona
# ping database → funciona (alias)

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

--network-alias añade nombres DNS extra. Varios contenedores con el mismo alias = DNS round-robin (load balancing simple). Ideal para service discovery sin herramientas externas. Funciona en redes personalizadas.

Docker Compose


10 cards
Estructura del 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:

Archivo YAML con la sección services (contenedores), volumes (datos) y networks (opcional). Cada servicio tiene image o build. depends_on define el orden de arranque. Compose crea una red automática.

Redes en 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:

Los servicios solo se comunican en la misma network. El ejemplo aísla: frontend habla con api, api habla con db, pero frontend no accede a db directamente. Compose crea una red por defecto automáticamente si no se define ninguna.

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

# Activar:
# docker compose watch
# o: docker compose up --watch -d

docker compose watch (Compose 2.22+) sincroniza los cambios en tiempo real. action: sync copia archivos sin rebuild. action: rebuild reconstruye la imagen. Sustituye a los bind mounts en muchos casos. Más rápido y fiable.

Comandos esenciales
docker compose up -d          # crear e iniciar (segundo plano)
docker compose down           # parar y eliminar todo
docker compose ps             # listar servicios
docker compose logs -f        # logs en tiempo real
docker compose exec web sh    # shell en un servicio
docker compose build          # rebuild de imágenes
docker compose pull           # pull de todas las imágenes
docker compose restart api    # reiniciar un servicio

up -d crea la red, los volúmenes e inicia todo. down elimina contenedores y red (los volúmenes quedan). logs -f sigue todos los servicios. exec abre una shell. build reconstruye imágenes con build local.

Perfiles y overrides
# docker-compose.yml con profiles:
# services:
#   debug-tools:
#     profiles: ["debug"]
#     image: nicolaka/netshoot

# Iniciar solo los servicios base:
docker compose up -d

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

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

profiles activan servicios condicionalmente. Sin --profile, los servicios con perfil no arrancan. -f superpone archivos (override). Patrón: base + override dev/prod. El segundo archivo sobrescribe al primero.

Logs y debugging
# Logs de todos los servicios:
docker compose logs -f

# Servicio específico:
docker compose logs -f api

# Últimas N líneas:
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 los logs de todos los servicios. --tail limita las líneas. exec abre una shell en un servicio. compose config valida y muestra el YAML resuelto (con overrides y .env aplicados).

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

  # O simple:
  worker:
    build: ./worker

build puede ser un string (ruta) o un objeto con context, dockerfile, args. target selecciona la etapa en multi-stage. docker compose build reconstruye. up --build hace build + up.

# docker-compose.yml
services:
  app:
    image: my-app:${TAG:-latest}
    environment:
      - DATABASE_URL=${DB_URL}
      - SECRET=${SECRET_KEY}

# .env (mismo directorio, cargado automáticamente):
TAG=2.1.0
DB_URL=postgres://user:pass@db:5432/app
SECRET_KEY=super-secret

Compose carga .env automáticamente para la sustitución de variables. ${VAR:-default} define un fallback. environment pasa vars al contenedor. Nunca commitear .env con secretos — usar .env.example.

Escalar servicios
# Escalar (múltiples réplicas):
docker compose up -d --scale worker=3

# Ver las réplicas:
docker compose ps

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

# Limitación (sin puerto fijo para escalar):
# ports: NO usar "8080:80" si escalas
# Usar rango: "8080-8082:80" o sin ports

--scale worker=3 crea 3 réplicas del servicio. No funciona con ports fijos (conflicto). Usa un rango de puertos o service discovery interno. Ideal para workers y procesamiento paralelo. Load balancing vía DNS.

depends_on y 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 con condition: service_healthy espera a que el healthcheck pase antes de iniciar los dependientes. healthcheck define comando, intervalo y retries. service_started solo espera el arranque (sin verificar la salud).

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

  db:
    image: postgres:16
    restart: always

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

# Opciones:
# no           → nunca reinicia (por defecto)
# always       → siempre (incluso tras stop manual)
# on-failure   → solo si exit code != 0
# unless-stopped → siempre excepto stop manual

restart define la política de reinicio. unless-stopped es la más usada (reinicia excepto si se paró manualmente). on-failure:N limita los intentos. always reinicia incluso tras docker restart. Esencial en producción.

Dockerfile


10 cards
FROM e imágenes base
# Imagen base oficial:
FROM node:20-alpine

# Con versión específica:
FROM python:3.12-slim

# Imagen mínima:
FROM alpine:3.19

# Scratch (vacía, para binarios):
FROM scratch

# Elección: alpine (5MB) < slim (30MB) < full (300MB+)
# Alpine usa musl libc (puede tener incompatibilidades)

FROM define la imagen base (primera instrucción). alpine es mínima (~5MB). slim elimina herramientas innecesarias. scratch está vacía (para binarios Go/Rust). Elige la menor que funcione para tu app.

WORKDIR y EXPOSE
# Definir el directorio de trabajo:
WORKDIR /app

# Todo lo siguiente es relativo a /app:
COPY package.json .
RUN npm install
COPY . .

# Documentar el puerto (¡NO publica!):
EXPOSE 3000
EXPOSE 5432/tcp
EXPOSE 53/udp

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

WORKDIR define el directorio (lo crea si no existe). Evita cd en RUN. EXPOSE es documentación — no publica el puerto. La publicación real es con -p en el run o ports en Compose. Usa siempre WORKDIR.

.dockerignore
# .dockerignore (en la raíz del build context)
node_modules
.git
.env
*.md
dist/
coverage/
.vscode
docker-compose*.yml
Dockerfile*

# Reduce el build context (más rápido)
# Evita copiar secretos (.env)
# Similar al .gitignore

.dockerignore excluye archivos del build context. Reduce el tiempo de build (menos datos enviados al daemon). Evita copiar .env, node_modules, .git. Sintaxis igual al .gitignore. Crea uno siempre.

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

# Múltiples comandos en una capa:
RUN npm ci --production \
    && npm cache clean --force

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

RUN ejecuta comandos durante el build. Cada RUN crea una capa — combina con && para minimizar. rm -rf /var/lib/apt/lists/* limpia la caché. --no-cache en apk evita la caché. Menos capas = imagen más pequeña.

CMD y ENTRYPOINT
# CMD: comando por defecto (puede sobrescribirse)
CMD ["node", "server.js"]

# ENTRYPOINT: binario fijo (los args se pasan)
ENTRYPOINT ["python", "app.py"]

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

# Sobrescribir CMD en el run:
# docker run my-app --debug

CMD define el comando por defecto (sustituible en el run). ENTRYPOINT es fijo — los argumentos se anexan. Combinación: ENTRYPOINT como binario, CMD como args por defecto. El formato JSON ["exec"] es el preferido (sin shell wrapper).

LABEL y STOPSIGNAL
# Metadata:
LABEL maintainer="dev@company.com"
LABEL version="2.1.0"
LABEL description="API de gestión de clientes"

# Visible en: docker inspect --format="{{.Config.Labels}}"

# Señal de parada:
STOPSIGNAL SIGTERM

# Para apps que usan SIGQUIT:
STOPSIGNAL SIGQUIT

LABEL añade metadata (autor, versión, descripción). Visible vía inspect. Útil para catalogar imágenes. STOPSIGNAL define la señal de shutdown (por defecto: SIGTERM). Nginx usa SIGQUIT para un stop graceful.

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

# ADD (extrae tar automáticamente):
ADD app.tar.gz /opt/app/

# Desde una URL (solo ADD):
ADD https://example.com/file.zip /tmp/

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

COPY copia archivos locales (transparente, preferido). ADD añade features: extracción de tar y descarga de URL. --chown define el dueño. Copia solo lo necesario — cada COPY invalida la caché si el archivo cambia.

USER y permisos
# Crear un usuario no-root:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

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

# Cambiar a non-root:
USER appuser

# A partir de aquí, todo corre como appuser:
RUN whoami    # appuser
CMD ["node", "server.js"]

USER define el usuario para los comandos siguientes. Nunca correr como root en producción. Crea el usuario con adduser -S (Alpine) o useradd -r (Debian). --chown en COPY garantiza permisos correctos.

ENV y ARG
# ENV: variable en runtime (persiste en el contenedor)
ENV NODE_ENV=production
ENV APP_PORT=3000

# ARG: variable solo durante el build
ARG VERSION=1.0.0
ARG BUILD_DATE

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

# ¡ARG no existe en runtime!
# Para pasarla al runtime: ENV MY_VAR=$ARG_VAR

ENV define variables que existen en el contenedor en runtime. ARG solo existe durante el build (no persiste). Usa ARG para versiones y configs de build. ENV para configs de la aplicación. Ambas accesibles vía $VAR.

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

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

# Estados: starting → healthy / unhealthy
# Visible en: docker ps (columna STATUS)

HEALTHCHECK define la verificación de salud. --interval frecuencia, --timeout límite, --retries fallos hasta unhealthy. --start-period da tiempo para arrancar. Docker y Compose lo usan para restart y depends_on.

Registry e Distribuição


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

# Tag con username:
docker tag my-app:1.0 myuser/my-app:1.0

# Push:
docker push myuser/my-app:1.0

# Pull en otro servidor:
docker pull myuser/my-app:1.0

# Logout:
docker logout

docker login autentica en Docker Hub. El tag debe incluir username/. push sube las capas. pull descarga. Las imágenes públicas son gratis; las privadas tienen límite en el plan free. Usa tokens en CI/CD.

Docker Content Trust
# Activar la firma de imágenes:
export DOCKER_CONTENT_TRUST=1

# El push ahora firma automáticamente:
docker push myuser/app:1.0

# El pull verifica la firma:
docker pull myuser/app:1.0

# Desactivar temporalmente:
docker pull --disable-content-trust myuser/app:1.0

Docker Content Trust (DCT) firma y verifica imágenes. Garantiza integridad y publisher. Actívalo con DOCKER_CONTENT_TRUST=1. El push crea la firma; el pull la verifica. Protege contra imágenes adulteradas.

Escaneo de vulnerabilidades
# Docker Scout (sucesor de docker scan):
docker scout cves my-app:1.0

# Resumen rápido:
docker scout quickview my-app:1.0

# Comparar versiones:
docker scout diff my-app:1.0 my-app:1.1

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

docker scout analiza CVEs en la imagen. quickview da un resumen. diff compara entre versiones. Trivy es una alternativa open-source. Intégralo en CI/CD para bloquear imágenes con vulnerabilidades críticas.

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

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

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

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

# Listar imágenes:
curl http://localhost:5000/v2/_catalog

registry:2 es el registry oficial self-hosted. Corre en el puerto 5000. El tag incluye host:puerto/. Sin auth por defecto (añadir TLS + htpasswd en producción). API REST en /v2/ para la gestión.

Imágenes multi-registry
# Tag para múltiples 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 a todos:
docker push ghcr.io/user/app:1.0
docker push registry.gitlab.com/user/app:1.0

# Mismo IMAGE ID, registries diferentes

Una imagen puede tener tags en múltiples registries. Mismo IMAGE ID, destinos diferentes. Útil para mirror (redundancia) o migración. Cada push envía solo las capas que el registry no tiene (deduplicación).

Manifest y attestation
# Ver el manifest (plataformas, capas):
docker manifest inspect nginx:latest

# Crear una manifest list manual:
docker manifest create myuser/app:1.0 \
  myuser/app:1.0-amd64 \
  myuser/app:1.0-arm64

docker manifest push myuser/app:1.0

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

El manifest describe las plataformas y capas de una imagen. manifest create une imágenes multi-arch. La attestation añade SBOM y provenance al build. Aumenta la transparencia y la seguridad en la supply chain.

Login en 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 myregistry

Cada cloud tiene su propio método de login. --password-stdin evita secretos en el historial. ghcr.io usa PAT. ECR usa un token temporal de la AWS CLI. Las credenciales quedan en ~/.docker/config.json.

Registry con autenticación
# Crear el archivo de contraseñas:
docker run --rm --entrypoint htpasswd httpd:2 -Bbn user pass > auth/htpasswd

# Registry con 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

Un registry en producción necesita TLS + htpasswd. Crea el archivo con htpasswd -Bbn. Configura vía variables de entorno. Sin TLS, Docker rechaza el push (insecure-registry solo para dev).

Tags y versionado
# Buenas prácticas de tags:
docker tag app my-app:1.2.3       # semver
docker tag app my-app:latest      # última
docker tag app my-app:$(git rev-parse --short HEAD)  # git SHA

# Push de múltiples tags:
docker push myuser/app:1.2.3
docker push myuser/app:latest

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

Versionado: usa semver (1.2.3) + latest para la más reciente. En CI/CD, etiqueta con el git SHA para trazabilidad. Nunca usar solo latest en producción — puede cambiar inesperadamente.

Garbage collection
# Limpiar blobs no referenciados en el registry:
docker exec registry registry garbage-collect /etc/docker/registry/config.yml

# Parar el registry primero (más 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 el espacio:
docker system df

garbage-collect elimina blobs huérfanos del registry (capas sin tag). Ocurre tras borrar tags. No libera espacio inmediatamente — necesita GC. Ejecútalo con el registry parado para mantener la consistencia.

Sistema e Manutenção


10 cards
docker info y version
# Información del daemon:
docker info

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

# Versión (client + server):
docker version

# Solo versión corta:
docker --version

docker info muestra el estado completo: nº de contenedores, imágenes, storage driver, memoria, CPUs. docker version muestra la versión del client y del server. El primer comando para diagnosticar problemas.

Eventos en tiempo real
# Todos los eventos:
docker events

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

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

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

docker events es un stream de eventos del daemon: create, start, stop, die, pull. --filter por tipo, evento o imagen. Útil para auditoría, alertas y automatización. Muestra timestamp, acción y metadata.

Troubleshooting común
# "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 quién usa el puerto

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

# El contenedor no arranca:
docker logs container
docker inspect --format="{{.State.Error}}" container

Errores comunes: daemon parado (systemctl start), permisos (usermod), puerto ocupado (lsof), disco lleno (prune). Verifica siempre docker logs e inspect para los detalles del error.

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

# Detallado:
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 muestra el espacio por tipo. RECLAIMABLE indica lo que puede limpiarse. -v lista cada elemento. El Build Cache acumula mucho en CI/CD. Verifica regularmente para evitar el disco lleno.

Reiniciar el daemon con seguridad
# Sin live-restore: ¡todos los contenedores se paran!
sudo systemctl restart docker

# Con live-restore (daemon.json):
# { "live-restore": true }
# → los contenedores siguen activos durante el restart

# Verificar tras el restart:
docker ps
docker info

# Recargar la config sin restart:
sudo systemctl reload docker

restart docker para todos los contenedores (sin live-restore). Activa live-restore: true en el daemon.json para mantener los contenedores activos. reload aplica la config sin reiniciar. Verifica siempre docker ps después.

Actualizar sin downtime
# 1. Activar live-restore en el daemon.json:
# { "live-restore": true }
sudo systemctl reload docker

# 2. Actualizar los paquetes:
sudo apt-get install docker-ce docker-ce-cli containerd.io

# 3. Reiniciar (los contenedores continúan):
sudo systemctl restart docker

# 4. Verificar:
docker ps    # ¿todos siguen activos?
docker info  # ¿versión nueva?

Con live-restore: true, los contenedores sobreviven al restart del daemon. Actualiza los paquetes y reinicia — las apps siguen activas. Sin live-restore, hay downtime. En producción con Swarm/K8s, haz un rolling update.

Prune (limpieza general)
# Limpiar todo lo que no está en uso:
docker system prune

# Incluir volúmenes (CUIDADO - ¡datos!):
docker system prune --volumes

# Incluir imágenes sin contenedores:
docker system prune -a

# Sin confirmación:
docker system prune -af

# Solo build cache:
docker builder prune

docker system prune elimina contenedores parados, redes huérfanas e imágenes dangling. -a incluye imágenes sin contenedores. --volumes elimina volúmenes (¡peligroso!). builder prune limpia la caché de build. Ejecútalo periódicamente.

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

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

# Ver el tamaño de los logs:
sudo du -sh /var/lib/docker/containers/*/*-json.log

¡Los logs pueden llenar el disco! Configura max-size y max-file en el daemon.json (global) o por contenedor. json-file es el driver por defecto. Alternativas: syslog, fluentd, local.

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

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

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

# Logs de contenedores:
docker logs --tail 100 -f container-name

En Linux, logs del daemon vía journalctl -u docker.service. Muestra errores de arranque, OOM, problemas de red. En Docker Desktop, logs en la UI. Para contenedores: docker logs. Esencial para el troubleshooting.

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

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

# Drivers disponibles:
# overlay2  → recomendado (Linux 4.0+)
# fuse-overlayfs → rootless
# btrfs/zfs → avanzados (snapshots nativos)

# Verificar el soporte:
grep overlay /proc/filesystems

overlay2 es el storage driver recomendado. Usa capas con copy-on-write. fuse-overlayfs para rootless. Nunca cambies de driver con datos existentes (lo pierdes todo). Verifica antes el soporte en el kernel.

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: Producción (imagen 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últiples FROM. Copia solo los artefactos con --from=builder. La imagen final no tiene node_modules, source, etc. Reduce de ~1GB a ~25MB. Patrón esencial para producción.

Init y manejo de señales
# Problema: PID 1 no gestiona zombies/señales
# Solución: usar init:
docker run -d --init my-app

# O en el Dockerfile:
# ENTRYPOINT ["docker-init", "--", "node", "server.js"]

# Verificar las señales:
docker stop app    # envía SIGTERM
# La app debe tratar SIGTERM para un graceful shutdown

# Sin init: los procesos zombie se acumulan

--init inyecta tini como PID 1. Gestiona señales y procesos zombie correctamente. Sin init, el proceso de la app es PID 1 y puede ignorar SIGTERM. Usa siempre --init o trata las señales en la app.

Optimizar imágenes
# 1. Base mínima:
FROM node:20-alpine

# 2. Orden para la caché (cambia menos → primero):
COPY package*.json ./
RUN npm ci --production
COPY . .

# 3. Multi-stage (no llevar el 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 (sin node_modules, .git)

Optimización: base alpine, multi-stage, orden de COPY para la caché, combinar RUN, .dockerignore. Copia package.json antes del source (caché de npm install). Objetivo: imagen más pequeña, build más rápido.

BuildKit y caché avanzada
# syntax=docker/dockerfile:1

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

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

# Secreto en el build (sin quedar en la imagen):
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 (caché persistente entre builds) y --mount=type=secret (secretos sin persistir en la imagen). Builds paralelos y más rápidos. Activo por defecto en Docker 23+.

docker buildx
# Crear un builder:
docker buildx create --name my-builder --use

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

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

# Inspeccionar el builder:
docker buildx inspect --bootstrap

# Eliminar:
docker buildx rm my-builder

buildx extiende el build: multi-platform, caché remota, output personalizado. --platform define las arquitecturas. --push envía directo al registry. -o type=docker carga localmente. Esencial para ARM + AMD.

Docker en CI/CD
# GitHub Actions (ejemplo):
# - 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 }}

# Caché de capas:
docker build --cache-from user/app:latest -t user/app:new .

# Probar antes del push:
docker run --rm user/app:new npm test

# Solo push si los tests pasan

En CI/CD: build con el tag del git SHA, prueba, y solo después push. --cache-from usa la imagen anterior como caché. Login con secrets (nunca hardcoded). --rm limpia el contenedor de prueba. Estándar para deploys fiables.

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

# Crear un servicio:
docker service create --name web --replicas 3 -p 80:80 nginx

# Listar servicios:
docker service ls
docker service ps web    # tareas

# Escalar:
docker service scale web=5

# Rolling update:
docker service update --image nginx:1.25 web

Swarm es la orquestación nativa de Docker. swarm init crea el clúster. service create con --replicas distribuye en los nodos. scale ajusta las réplicas. update hace un rolling update. Más simple que Kubernetes.

Scripts de entrypoint
# entrypoint.sh
#!/bin/sh
set -e

# Esperar la BD:
until pg_isready -h db -p 5432; do
  echo "Esperando la BD..."
  sleep 2
done

# Ejecutar migrations:
npm run migrate

# Ejecutar el comando principal:
exec "$@"

Los scripts de entrypoint preparan el entorno antes de la app. Esperan dependencias, corren migrations, configuran el env. exec "$@" sustituye la shell por el CMD (recibe señales). set -e para en errores. Estándar en producción.

Healthcheck avanzado
# En Compose con depends_on:
# app depende de que db esté healthy

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

# Estados: starting → healthy | unhealthy

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

Health.Status muestra starting/healthy/unhealthy. Health.Log tiene el output de las verificaciones. Compose lo usa para depends_on condition. Puede definirse en el Dockerfile o en el run. Esencial para el auto-healing.

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

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

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

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

Docker expone una API REST vía /var/run/docker.sock. Todo lo que hace el CLI, lo hace la API. Versión: /v1.45/. Usada por herramientas (Portainer, Traefik). Cuidado: acceso al socket = acceso root.

Instalação e Setup


10 cards
Instalar en Linux (Ubuntu/Debian)
# Eliminar versiones antiguas
sudo apt-get remove docker docker-engine docker.io

# Instalar dependencias
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg

# Añadir la clave 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

# Añadir el repositorio
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 Docker Engine en Ubuntu/Debian vía el repositorio oficial. El docker-compose-plugin incluye Compose V2. Usa siempre el repositorio oficial para recibir actualizaciones de seguridad.

Primer contenedor (hello-world)
docker run hello-world

# Qué ocurre:
# 1. Docker búsqueda la imagen localmente
# 2. No la encuentra → hace pull de Docker Hub
# 3. Crea un contenedor a partir de la imagen
# 4. Ejecuta el binario → imprime un mensaje
# 5. El contenedor termina (exit 0)

docker ps -a    # ver el contenedor (estado Exited)

hello-world demuestra el ciclo completo: pullcreaterunexit. El contenedor se detiene tras ejecutarse. docker ps -a lo muestra como Exited. Base para entender el modelo de contenedores.

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

# Verificar la versión:
docker --version

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

# Notas de versión:
# https://docs.docker.com/engine/release-notes/

En Linux, actualiza vía apt-get (el mismo comando de la instalación). En Windows/Mac, Docker Desktop se actualiza automáticamente o vía winget. Mantenlo actualizado para parches de seguridad y nuevas features.

Instalar en Windows y Mac
# Windows / Mac: Docker Desktop
# Descarga: https://www.docker.com/products/docker-desktop/

# Windows: requiere WSL2 (Windows Subsystem for Linux)
wsl --install              # activar WSL2
# Después instalar Docker Desktop (instalador .exe)

# Mac (Apple Silicon o Intel):
# Descargar .dmg → arrastrar a Applications

# Verificar la instalación:
docker --version
docker compose version

Docker Desktop es la forma recomendada para Windows y Mac. En Windows usa WSL2 como backend (más rápido que Hyper-V). En Mac usa una VM ligera. Incluye Docker Engine, Compose y BuildKit.

docker run (primer servidor)
# Nginx en segundo plano, puerto 8080 → 80
docker run -d --name web -p 8080:80 nginx

# Abrir en el navegador: http://localhost:8080

# Ver los logs:
docker logs web

# Detener y eliminar:
docker stop web
docker rm web

docker run -d ejecuta en segundo plano (detached). -p 8080:80 mapea el puerto host→contenedor. --name da un nombre amigable. Nginx sirve la página por defecto en localhost:8080. Primer paso para servir aplicaciones.

Conceptos fundamentales
# Imagen     → plantilla read-only (ej: nginx:alpine)
# Contenedor → instancia en ejecución de una imagen
# Volumen    → datos persistentes fuera del contenedor
# Red        → comunicación entre contenedores
# Registry   → repositorio de imágenes (Docker Hub)
# Daemon     → servicio Docker (dockerd)
# Client     → CLI que habla con el daemon (docker)

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

La imagen es la plantilla; el contenedor es la instancia viva. Los volúmenes persisten datos. Las redes conectan contenedores. El daemon (dockerd) lo ejecuta todo. El client (docker CLI) envía comandos vía API REST.

Verificar la instalación
docker --version          # Docker version 27.x.x
docker compose version    # Docker Compose version v2.x.x
docker info               # detalles del daemon
docker run hello-world    # prueba completa

# Si "permission denied" en Linux:
sudo usermod -aG docker $USER
newgrp docker             # aplicar sin logout

docker --version confirma la instalación. docker run hello-world prueba el flujo completo (pull + create + run). En Linux, añade el usuario al grupo docker para evitar sudo.

Servicio Docker (systemd)
# Gestionar el daemon como servicio:
sudo systemctl start docker
sudo systemctl stop docker
sudo systemctl restart docker
sudo systemctl status docker

# Arrancar automáticamente en el boot:
sudo systemctl enable docker

# Verificar si está activo:
systemctl is-active docker    # "active"

En Linux, Docker corre como servicio systemd. enable garantiza el arranque en el boot. status muestra el estado y el PID. En Docker Desktop (Windows/Mac), la aplicación gestiona el servicio automáticamente.

Configurar el daemon (daemon.json)
# /etc/docker/daemon.json (Linux)
# o 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 tras los cambios:
sudo systemctl restart docker

El archivo daemon.json configura Docker globalmente. log-opts limita los logs (evita el disco lleno). overlay2 es el storage driver recomendado. live-restore mantiene los contenedores activos al reiniciar el daemon.

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

# Crear un contexto remoto:
docker context create production --docker "host=ssh://user@192.168.1.100"

# Cambiar de contexto:
docker context use production

# Los comandos ahora corren en el host remoto:
docker ps    # lista los contenedores del servidor remoto

# Volver al local:
docker context use default

Docker Context permite gestionar múltiples hosts sin cambiar variables de entorno. create define un endpoint (local o SSH). use cambia el destino. Ideal para gestionar servidores remotos con los mismos comandos.

Volumes


10 cards
Crear y listar volúmenes
# Crear un volumen con nombre:
docker volume create db-data

# Listar:
docker volume ls

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

# Detalles:
docker volume inspect db-data
# Muestra: Mountpoint, Driver, Labels, Scope

docker volume create crea un volumen gestionado por Docker. ls lista todos. dangling=true muestra los no usados. Los datos quedan en /var/lib/docker/volumes/. Sobreviven a la eliminación de contenedores.

Compartir datos entre contenedores
# El mismo volumen en varios contenedores:
docker volume create shared
docker run -d --name app1 -v shared:/data app
docker run -d --name app2 -v shared:/data app

# Ambos leen/escriben en /data

# Volumen de otro contenedor (deprecated):
docker run -d --volumes-from app1 new-app

Varios contenedores pueden montar el mismo volume. Las lecturas y escrituras se comparten. Cuidado con la concurrencia (sin locking). --volumes-from hereda los mounts de otro contenedor (legado). Prefiere volúmenes con nombre.

Volúmenes vs Bind Mounts
# Volumen (gestionado por Docker):
docker run -v data:/var/lib/mysql db
# + Portátil, backup fácil, drivers
# + Funciona igual en Linux/Mac/Win

# Bind mount (carpeta del host):
docker run -v $(pwd)/src:/app/src app
# + Hot-reload en desarrollo
# - Depende de la estructura del host
# - Los permisos pueden entrar en conflicto

Los volúmenes los gestiona Docker (portátiles, backup, drivers). Los bind mounts enlazan una carpeta del host (hot-reload, debug). En producción: volúmenes. En desarrollo: bind mounts para el código. Nunca bind mount para datos de BD.

Montar un volumen en el run
# Volumen con nombre:
docker run -d -v db-data:/var/lib/postgresql/data postgres

# Sintaxis: -v nombre_volumen:/ruta/contenedor

# Si el volumen no existe, Docker lo crea automáticamente:
docker run -d -v new-vol:/data alpine

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

-v volumen:/ruta monta un volumen con nombre. Si no existe, se crea automáticamente. Los datos persisten entre restarts y eliminaciones. --mount es la sintaxis explícita (preferida en producción). La ruta en el contenedor es absoluta.

Backup de volumen
# Backup: volumen → tar en el host
docker run --rm -v db-data:/source -v $(pwd):/backup alpine \
  tar czf /backup/db-backup.tar.gz -C /source .

# Restore: tar → volumen
docker run --rm -v db-data:/target -v $(pwd):/backup alpine \
  tar xzf /backup/db-backup.tar.gz -C /target

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

El backup usa un contenedor temporal con dos mounts: volumen origen + carpeta destino. tar czf comprime. El restore lo invierte: tar del host → volumen. --rm elimina el contenedor tras la operación. Patrón esencial para datos críticos.

Permisos y ownership
# El contenedor corre como UID 1000:
docker run -d --user 1000:1000 -v data:/data app

# Corregir permisos en el Dockerfile:
# RUN chown -R node:node /app/data

# Verificar el dueño de los archivos:
docker exec app ls -la /data

# Problema común: archivos creados como root
# Solución: --user o chown en el entrypoint

Problema común: archivos creados como root dentro del contenedor. Solución: --user UID:GID en el run. O chown en el Dockerfile. Los bind mounts heredan los permisos del host. Los volúmenes nuevos son root por defecto.

Bind mount (carpeta del host)
# Bind mount: carpeta local → contenedor
docker run -d -v $(pwd)/src:/app/src node:20

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

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

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

Bind mount mapea una carpeta del host al contenedor. Los cambios son bidireccionales e inmediatos. :ro lo hace read-only. Ideal para desarrollo (hot-reload). --mount type=bind es más explícito y seguro.

Montaje tmpfs
# Montar en RAM (no persiste):
docker run -d --tmpfs /tmp:rw,size=100m my-app

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

# Casos de uso:
# • Caché temporal rápida
# • Datos sensibles (nunca tocan el disco)
# • Archivos de sesión

tmpfs monta en RAM — ultrarrápido pero volátil. Los datos desaparecen al parar el contenedor. tmpfs-size limita el tamaño en bytes. Ideal para caché, sesiones y datos sensibles que no deben tocar el disco.

Eliminar y limpiar volúmenes
# Eliminar un volumen:
docker volume rm db-data

# Eliminar los volúmenes del contenedor al borrarlo:
docker rm -v my-container

# Limpiar todos los no utilizados:
docker volume prune

# Eliminar un volumen específico de un contenedor:
docker rm -v --force container-with-data

volume rm elimina el volumen (¡datos perdidos!). docker rm -v elimina los volúmenes anónimos del contenedor. prune limpia los dangling. Los volúmenes con nombre NO se eliminan con rm -v — solo con volume rm explícito.

Drivers de volumen
# Driver local (por defecto):
docker volume create --driver local my-data

# Con opciones (NFS, por ejemplo):
docker volume create --driver local \
  --opt type=nfs \
  --opt o=addr=192.168.1.50,rw \
  --opt device=:/exports/data \
  nfs-vol

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

El driver local es el predeterminado. Soporta opciones como NFS, tmpfs. Drivers de terceros: Azure File, AWS EBS, Ceph. --opt pasa configuraciones del driver. Permite volúmenes en red compartidos entre hosts.

Segurança e Recursos


10 cards
Limitar memoria
# Límite de RAM:
docker run -d --memory 512m my-app

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

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

# Ver el uso:
docker stats my-app

# OOM Kill (sin límite): el kernel mata el proceso

--memory limita la RAM máxima. --memory-swap limita RAM+swap. --memory-reservation es un soft limit. Sin límite, un contenedor puede consumir toda la RAM. En producción, define siempre límites.

Capabilities (Linux)
# Quitar todas y añadir solo las necesarias:
docker run -d --cap-drop ALL --cap-add NET_BIND_SERVICE app

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

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

--cap-drop ALL elimina privilegios de Linux. --cap-add añade solo lo necesario (ej: NET_BIND_SERVICE para el puerto 80). --privileged da acceso total — nunca en producción. Principio del menor privilegio.

Aislamiento de red
# Sin red externa (solo interna):
docker network create --internal isolated-net

docker run -d --network isolated-net db

# Sin acceso a internet:
docker run -d --network none app

# DNS personalizado:
docker run -d --dns 8.8.8.8 --dns-search company.local app

# Hostname personalizado:
docker run -d --hostname my-server app

--internal crea una red sin acceso externo (sin gateway). --network none aísla totalmente. --dns define el servidor DNS. --hostname personaliza el nombre interno. Combínalos para seguridad en capas.

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

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

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

# Peso relativo (shares):
docker run -d --cpu-shares 512 app   # por defecto: 1024

--cpus limita las CPUs (acepta decimales). --cpuset-cpus fija a cores específicos. --cpu-shares define la prioridad relativa. Evita que un contenedor monopolice la CPU. Monitoriza con docker stats.

Secrets (sin env vars)
# Malo: secretos en env (visibles en inspect):
docker run -e DB_PASS=secret app

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

# Secrets de Docker Swarm:
docker secret create db_pass ./db_pass.txt
docker service create --secret db_pass my-app

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

Evita secretos en ENV (visibles en inspect y logs). Móntalos como archivo read-only en /run/secrets/. En Swarm, usa docker secret (cifrado en tránsito y en reposo). En Compose, la sección secrets.

Aislamiento de PID e IPC
# Aislar el namespace PID (por defecto):
docker run -d my-app    # ya aislado

# Compartir el PID del host (debug):
docker run -d --pid host diagnostic-tool

# Aislamiento IPC:
docker run -d --ipc private my-app

# Compartir memoria (apps que usan shared memory):
docker run -d --ipc host my-app

# UTS (hostname aislado por defecto):
docker run -d --hostname app1 my-app

Docker aísla PID, IPC, UTS, mount y network por defecto (namespaces). --pid host elimina el aislamiento (solo debug). --ipc host para apps con shared memory. Mantén el aislamiento en producción.

Usuario non-root
# En el Dockerfile:
# RUN adduser -D appuser
# USER appuser

# En el run (override):
docker run -d --user 1000:1000 my-app

# Verificar el usuario actual:
docker exec app whoami
docker exec app id

# Ver el usuario configurado en la imagen:
docker inspect --format="{{.Config.User}}" my-app

Correr como root es un riesgo de seguridad. Define USER en el Dockerfile o --user en el run. Si ocurre un exploit, el atacante queda limitado a ese usuario. Usa siempre non-root en producción.

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

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

# Verificar:
docker info | grep -i rootless

# Ventajas:
# • El daemon no corre como root
# • Un exploit no da acceso al host

Rootless Docker corre el daemon como usuario normal (sin root). Incluso con un exploit, el atacante no gana root en el host. Usa user namespaces. Limitaciones: sin puertos <1024, sin algunas redes. Recomendado por seguridad.

Filesystem read-only
# Sistema de archivos read-only:
docker run -d --read-only my-app

# Con tmpfs para escritura temporal:
docker run -d --read-only --tmpfs /tmp --tmpfs /var/run app

# Volumen para datos persistentes:
docker run -d --read-only -v data:/data app

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

--read-only hace el filesystem inmutable. Impide que el malware modifique archivos. Usa --tmpfs para los dirs que necesitan escritura (/tmp, /var/run). Combina con volúmenes para los datos. Seguridad máxima.

Security options
# No-new-privileges (impide la escalación):
docker run -d --security-opt no-new-privileges:true app

# Perfil de AppArmor:
docker run -d --security-opt apparmor=my-profile app

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

# Perfil seccomp personalizado:
docker run -d --security-opt seccomp=profile.json app

no-new-privileges impide setuid/escalación. AppArmor/SELinux aplican MAC (mandatory access control). seccomp filtra syscalls. Docker ya aplica perfiles por defecto — personalízalos para apps sensibles.