Perfiles y Entornos Múltiples

Lectura
20 min~8 min lectura
CONCEPTO CLAVE: Los perfiles en Docker Compose permiten activar o desactivar servicios específicos según el contexto de ejecución, facilitando la gestión de entornos de desarrollo, pruebas y producción desde un único archivo compose.

Introducción a los Perfiles en Docker Compose

Cuando trabajamos en proyectos de desarrollo, frecuentemente necesitamos servicios que no siempre deben estar activos. Por ejemplo, durante el desarrollo local podrías querer tener un panel de administración visual como Adminer o pgAdmin, pero estos servicios no tienen sentido en producción. Los perfiles resuelven este problema eleganteamente.

En esta lección aprenderás a:

  • Definir y usar perfiles en docker-compose.yml
  • Crear entornos múltiples (desarrollo, staging, producción)
  • Combinar perfiles con variables de entorno
  • Optimizar el rendimiento de tu flujo de trabajo

¿Qué son los Perfiles?

Los perfiles son etiquetas que asignas a servicios en tu archivo docker-compose. Un servicio con un perfil solo se arrancará cuando lo solicites explícitamente con la bandera --profile o cuando el perfil esté activo mediante la variable de entorno COMPOSE_PROFILES.

📌 Los perfiles son especialmente útiles para servicios de soporte como bases de datos de pruebas, herramientas de monitoreo, generadores de datos fake, o interfaces gráficas que no necesitas en todos los entornos.

Sintaxis Básica de Perfiles

Veamos la estructura fundamental:

version: '3.9'

services:
  app:
    image: mi-aplicacion:latest
    ports:
      - "3000:3000"

  db:
    image: postgres:15
    environment:
      POSTGRES_DB: miapp_dev
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: dev123
    profiles:
      - development

  adminer:
    image: adminer
    ports:
      - "8080:8080"
    profiles:
      - development
      - debug

  redis:
    image: redis:alpine
    profiles:
      - cache

  mailhog:
    image: mailhog/mailhog
    ports:
      - "1025:1025"
      - "8025:8025"
    profiles:
      - debug

En este ejemplo:

  • app y db se inician siempre (sin perfil)
  • adminer se inicia con perfil development o debug
  • redis requiere el perfil cache
  • mailhog se inicia solo con el perfil debug

Activando Perfiles

Existen varias formas de activar perfiles:

Método 1: Bandera --profile

# Activar un solo perfil
docker-compose up -d --profile development

# Activar múltiples perfiles
docker-compose up -d --profile development --profile debug

# Forma corta (separados por coma)
docker-compose up -d --profile "development,debug"

Método 2: Variable de entorno

# Linux/Mac
export COMPOSE_PROFILES=development
docker-compose up -d

# Windows PowerShell
$env:COMPOSE_PROFILES="development"
docker-compose up -d

# Windows CMD
set COMPOSE_PROFILES=development
docker-compose up -d
💡 La variable de entorno es ideal cuando trabajas en un proyecto específico y siempre quieres los mismos perfiles activos. Puedes añadirla a tu archivo .env.local o .bashrc.

Método 3: docker-compose.override.yml

También puedes crear archivos de override por perfil:

# docker-compose.dev.yml
version: '3.9'

services:
  app:
    build:
      context: .
      target: development
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development
    command: npm run dev

  adminer:
    profiles:
      - dev-tools
    restart: unless-stopped

Entornos Múltiples con Docker Compose

Los entornos múltiples son fundamentales para cualquier proyecto serio. Necesitas poder cambiar entre configuración de desarrollo, staging y producción sin modificar tu código.

📌 La estrategia recomendada es tener un archivo base docker-compose.yml con la configuración común y archivos específicos para cada entorno.

Estructura de Archivos Recomendada

proyecto/
├── docker-compose.yml          # Base (siempre presente)
├── docker-compose.dev.yml     # Desarrollo
├── docker-compose.staging.yml  # Staging
├── docker-compose.prod.yml     # Producción
├── .env                        # Variables comunes
├── .env.dev                    # Variables desarrollo
├── .env.staging                # Variables staging
└── .env.prod                   # Variables producción

Ejemplo Práctico Completo

Creemos una aplicación Node.js con diferentes entornos:

# docker-compose.yml (BASE)
version: '3.9'

services:
  app:
    image: miapp:latest
    restart: unless-stopped

  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  postgres_data:
# docker-compose.dev.yml
version: '3.9'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development
      - DEBUG=true
      - LOG_LEVEL=debug
    ports:
      - "3000:3000"
    command: npm run dev
    profiles:
      - dev-tools

  adminer:
    image: adminer:latest
    ports:
      - "8080:8080"
    profiles:
      - dev-tools
    restart: unless-stopped

  mailhog:
    image: mailhog/mailhog:latest
    ports:
      - "1025:1025"
      - "8025:8025"
    profiles:
      - dev-tools
    restart: unless-stopped
# docker-compose.staging.yml
version: '3.9'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        NODE_ENV: staging
    environment:
      - NODE_ENV=staging
      - DEBUG=false
      - LOG_LEVEL=info
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: '0.5'
          memory: 512M

  db:
    environment:
      - POSTGRES_DB=miapp_staging
      - POSTGRES_USER=staging_user
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    secrets:
      - db_password

secrets:
  db_password:
    file: ./secrets/staging_db_password.txt
# docker-compose.prod.yml
version: '3.9'

services:
  app:
    image: miapp:production
    environment:
      - NODE_ENV=production
      - DEBUG=false
      - LOG_LEVEL=error
    deploy:
      replicas: 3
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
      resources:
        limits:
          cpus: '1.0'
          memory: 1G

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=miapp_production
      - POSTGRES_USER=prod_user
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    volumes:
      - prod_postgres_data:/var/lib/postgresql/data
    secrets:
      - db_password

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
    depends_on:
      - app
    profiles:
      - production

volumes:
  prod_postgres_data:
    driver: local-persist

secrets:
  db_password:
    file: ./secrets/prod_db_password.txt

Comandos para Cambiar Entre Entornos

  1. Desarrollo: docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d --profile dev-tools
  2. Staging: docker-compose -f docker-compose.yml -f docker-compose.staging.yml up -d
  3. Producción: docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d --profile production
⚠️ IMPORTANTE: En producción, nunca uses la bandera -d sin antes verificar que los servicios arrancan correctamente. Ejecuta primero docker-compose up sin -d para ver los logs en tiempo real y detectar problemas.

Variables de Entorno por Entorno

Usa archivos .env para mantener las configuraciones sensibles separadas:

# .env (común a todos los entornos)
APP_NAME=MiApp
APP_VERSION=1.0.0

# .env.dev
NODE_ENV=development
LOG_LEVEL=debug
API_URL=http://localhost:3001

# .env.staging
NODE_ENV=staging
LOG_LEVEL=info
API_URL=https://api.staging.miapp.com

# .env.prod
NODE_ENV=production
LOG_LEVEL=error
API_URL=https://api.miapp.com

Para usar un archivo .env específico:

# Desarrollo
docker-compose --env-file .env.dev -f docker-compose.yml -f docker-compose.dev.yml up

# Producción
docker-compose --env-file .env.prod -f docker-compose.yml -f docker-compose.prod.yml up
💡 Crea un alias en tu shell para facilitar estos comandos largos. Añade esto a tu ~/.bashrc o ~/.zshrc:
alias dc-dev="docker-compose -f docker-compose.yml -f docker-compose.dev.yml --env-file .env.dev"
alias dc-stage="docker-compose -f docker-compose.yml -f docker-compose.staging.yml --env-file .env.staging"
alias dc-prod="docker-compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod"

Tabla Resumen de Perfiles vs Entornos

CaracterísticaPerfiles (profiles)Archivos Compose Múltiples
Uso principalActivar/desactivar servicios opcionalesConfiguraciones de entorno diferentes
ÁmbitoServicios individualesServicios completos y configuración
Activación--profile o COMPOSE_PROFILES-f archivo1.yml -f archivo2.yml
Ejemplo típicoAdminer, Mailhog en desarrollodev vs staging vs production
FlexibilidadCombinar múltiples perfilesOverride completo de configuración

Casos de Uso Avanzados

Ver más: Perfiles con Condiciones de Dependencia

A veces necesitas que un servicio se ejecute solo si otro perfil está activo. Puedes lograr esto combinando perfiles con la opción profiles:

services:
  app:
    image: miapp
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 5
    profiles:
      - database

  redis:
    image: redis:alpine
    profiles:
      - cache

  elasticsearch:
    image: elasticsearch:8.11.0
    environment:
      - discovery.type=single-node
    profiles:
      - search
      - full-stack
    depends_on:
      - redis
Ver más: Perfiles para Testing

Crea un perfil específico para ejecutar tests:

services:
  app:
    image: miapp:test
    command: npm test

  app-ci:
    build:
      context: .
      target: test
    profiles:
      - ci
    depends_on:
      db-test:
        condition: service_started

  db-test:
    image: postgres:15
    environment:
      POSTGRES_DB: test_db
    profiles:
      - ci
    tmpfs:
      - /var/lib/postgresql/data

El tmpfs hace que los datos se guarden en memoria, ideal para tests que deben ser rápidos y no persistir datos.

Buenas Prácticas

Un archivo docker-compose.yml bien estructurado es documentación ejecutable de tu arquitectura de servicios.
  • Nombra los perfiles descriptivamente: Usa nombres como dev-tools, monitoring, ci en lugar de p1, p2
  • Documenta en comentarios: Explica qué hace cada perfil y cuándo usarlo
  • Separación de concerns: Un perfil no debería modificar servicios base si no es necesario
  • Versiona tus archivos compose: Los archivos compose son código, trátalos como tal
  • Usa .dockerignore: Evita copiar archivos innecesarios al contexto de build
⚠️ No confundas los perfiles de Docker Compose con los perfiles de build (target) en Dockerfiles. Los perfiles de Compose solo afectan qué servicios se inician, no cómo se construyen las imágenes.

Solución de Problemas Comunes

Ver más: Problemas Frecuentes

Problema: Un servicio con perfil no inicia

  • Verifica que el perfil esté correctamente escrito
  • Comprueba que no haya errores de sintaxis YAML (indentación)
  • Usa docker-compose config para validar

Problema: Dependencias con perfiles

# Incorrecto: db no existe si no está el perfil
services:
  app:
    depends_on:
      - db  # Fallará si db tiene un perfil que no está activo

# Correcto: db siempre presente o sin perfil
services:
  app:
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres
    # Sin perfil, siempre disponible

Problema: Variables de entorno no cargan

  • Verifica el orden de los archivos con -f
  • La variable en el archivo override tiene precedencia sobre el base
  • Usa docker-compose config para ver la configuración final

Comandos Útiles

# Ver qué perfiles están disponibles
docker-compose config --profiles

# Iniciar con múltiples perfiles
docker-compose up -d --profile dev --profile monitoring

# Ver solo servicios sin perfil (base)
docker-compose up -d

# Detener servicios de un perfil específico
docker-compose stop adminer mailhog

# Ver logs de servicios con perfil
docker-compose logs --profile debug

# Listar servicios activos
docker-compose ps
💡 El comando docker-compose config --profiles (disponible desde Docker Compose v2.17) lista todos los perfiles definidos en tu archivo, útil para documentar qué perfiles existen.

Conclusión

Los perfiles y los entornos múltiples son herramientas poderosas que te permiten:

  • Mantener un único archivo base para todos tus servicios
  • Activar únicamente los servicios que necesitas en cada momento
  • Separar claramente configuraciones de desarrollo, staging y producción
  • Mejorar el rendimiento al no cargar servicios innecesarios
  • Facilitar el trabajo en equipo con configuraciones predefinidas

Dominar estas técnicas te permitirá crear flujos de trabajo de desarrollo más limpios y profesionales, reduciendo la fricción entre diferentes contextos de ejecución.

🧠 Quiz

¿Cuál es la forma correcta de iniciar solo los servicios base (sin perfiles) usando docker-compose?

  • A) docker-compose up --no-profiles
  • B) docker-compose up -d
  • C) docker-compose start --profile none
  • D) docker-compose up -d --profile base
✅ Respuesta correcta: B) docker-compose up -d. Los servicios sin perfil definido siempre se inician por defecto. No necesitas banderas especiales; simplemente ejecutar docker-compose up arrancará todos los servicios base.
🧠 Quiz

¿Cuál es la ventaja principal de usar múltiples archivos docker-compose (ej: docker-compose.prod.yml) en lugar de solo perfiles?

  • A) Los perfiles son más lentos
  • B) Los archivos separados permiten hacer override completo de cualquier configuración incluyendo builds, recursos, y variables de entorno específicas del entorno
  • C) Los perfiles no funcionan en producción
  • D) Los archivos separados usan menos disco
✅ Respuesta correcta: B) Los archivos de override permiten modificar cualquier aspecto de la configuración para cada entorno, incluyendo builds específicos, límites de recursos, replicas, y configuración completa de servicios, algo que los perfiles por sí solos no pueden hacer de forma tan completa.