Los perfiles de Docker Compose permiten mantener servicios opcionales en un mismo compose.yaml y activarlos solo cuando hacen falta. Son útiles para herramientas de depuración, pruebas, migraciones, paneles administrativos u observabilidad.

Sin embargo, hay una distinción importante: un perfil selecciona servicios, pero no crea por sí mismo un entorno aislado. Para ejecutar desarrollo, staging y producción sin colisiones también se necesitan nombres de proyecto, configuraciones, redes, volúmenes y secretos independientes.
Qué vas a implementar
La configuración de ejemplo tendrá:
appydbcomo servicios principales, siempre disponibles.adminerdentro del perfildebug.smoke-testdentro del perfiltest.- Nombres de proyecto diferentes para cada entorno.
- Archivos de variables y overrides específicos.
- Comandos para validar la configuración antes de iniciarla.
Requisitos previos
Necesitas:
- Docker Engine instalado y activo.
- Docker Compose v2, invocado como
docker compose. - Permisos para administrar contenedores.
- Una imagen de la aplicación que escuche en el puerto interno
8080. - Un endpoint de comprobación, como
/health. - Secretos y datos de producción almacenados fuera del repositorio.
Comprueba las versiones instaladas:
docker version
docker compose version
docker infoEl borrador utilizaba docker compose info, pero ese no es el comando apropiado: la información del motor se obtiene con docker info.
Cómo funcionan los profiles
Los servicios que no tienen el atributo profiles están siempre habilitados. Los servicios perfilados solo se incorporan cuando se activa alguno de sus perfiles.
services:
app:
image: ejemplo/app
debugger:
image: ejemplo/debugger
profiles: ["debug"]En este caso:
docker compose upinicia únicamente app. En cambio:
docker compose --profile debug upincluye también debugger.
Docker recomienda dejar los servicios principales sin perfiles. Además, si se selecciona directamente un servicio perfilado, Compose lo ejecuta junto con sus dependencias, pero no inicia automáticamente los demás servicios que compartan ese perfil. Documentación oficial sobre profiles.
Paso 1: preparar la estructura
Trabaja primero en un entorno de prueba:
mkdir -p ~/compose-profiles/{secrets,config}
cd ~/compose-profiles
umask 077Estructura propuesta:
compose-profiles/
├── compose.yaml
├── compose.dev.yaml
├── compose.prod.yaml
├── .env.dev
├── .env.prod
└── secrets/
└── db_password.txtNo guardes contraseñas, tokens ni claves reales en Git. Para producción, utiliza el mecanismo de secretos disponible en tu plataforma. Los secretos de Compose basados en archivos son montajes controlados, no un gestor centralizado de secretos.
Paso 2: definir los servicios y perfiles
Crea compose.yaml:
services:
app:
image: ${APP_IMAGE:?Debes definir APP_IMAGE}
depends_on:
db:
condition: service_healthy
ports:
- "127.0.0.1:${APP_PORT:-8080}:8080"
networks:
- backend
db:
image: postgres:17-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- db_data:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
adminer:
image: adminer:standalone
profiles: ["debug"]
depends_on:
db:
condition: service_healthy
ports:
- "127.0.0.1:8081:8080"
networks:
- backend
smoke-test:
image: curlimages/curl:8.12.1
profiles: ["test"]
depends_on:
app:
condition: service_started
command:
- "--fail"
- "--silent"
- "--show-error"
- "--retry"
- "10"
- "--retry-connrefused"
- "http://app:8080/health"
networks:
- backend
networks:
backend:
volumes:
db_data:
secrets:
db_password:
file: ./secrets/db_password.txtAntes de ejecutarlo:
- Sustituye
APP_IMAGEpor una imagen aprobada. - Confirma que la aplicación escucha en
8080. - Ajusta
/healthsi utiliza otra ruta. - Fija las imágenes por digest en producción.
- No expongas Adminer públicamente; el binding a
127.0.0.1limita el acceso al propio servidor.
Paso 3: separar la configuración de cada entorno
Ejemplo de .env.dev:
APP_IMAGE=registro.ejemplo.com/miapp:dev
APP_PORT=8080Ejemplo de .env.prod:
APP_IMAGE=registro.ejemplo.com/miapp@sha256:DIGEST_VERIFICADO
APP_PORT=8080El archivo indicado mediante --env-file se utiliza para interpolar valores del modelo Compose. No introduce automáticamente todas esas variables dentro de los contenedores y no debe tratarse como almacén de secretos. Variables e interpolación en Compose.
Cuando un entorno necesite cambios estructurales, utiliza un archivo adicional. Por ejemplo, compose.prod.yaml:
services:
app:
restart: unless-stopped
db:
restart: unless-stoppedCompose combina los archivos en el orden especificado; los posteriores agregan o sobrescriben valores. Conviene revisar siempre el resultado final con docker compose config. Reglas oficiales de combinación.
Paso 4: validar antes de aplicar
Primero valida la configuración base:
docker compose \
-p miapp-dev \
--env-file .env.dev \
-f compose.yaml \
config --quietMuestra los perfiles declarados:
docker compose \
--env-file .env.dev \
-f compose.yaml \
config --profilesComprueba los servicios del perfil debug:
docker compose \
--profile debug \
--env-file .env.dev \
-f compose.yaml \
config --servicesPara inspeccionar el modelo efectivo de producción:
docker compose \
-p miapp-prod \
--env-file .env.prod \
-f compose.yaml \
-f compose.prod.yaml \
configdocker compose config resuelve variables, combina archivos y representa el modelo que Compose aplicará. La opción --quiet valida sin imprimirlo. Referencia del comando.
El comando docker build --check del borrador no valida un archivo Compose. Solo es pertinente cuando existe un Dockerfile que realmente se va a construir.
Paso 5: iniciar los servicios
Inicia únicamente los servicios principales:
docker compose \
-p miapp-dev \
--env-file .env.dev \
-f compose.yaml \
up -dActiva también el perfil de depuración:
docker compose \
-p miapp-dev \
--profile debug \
--env-file .env.dev \
-f compose.yaml \
up -dActiva varios perfiles:
docker compose \
-p miapp-dev \
--profile debug \
--profile test \
--env-file .env.dev \
-f compose.yaml \
up -dTambién pueden declararse mediante una variable:
COMPOSE_PROFILES=debug,test docker compose up -dPara habilitar todos los perfiles:
docker compose --profile "*" up -dEsta última opción es útil para pruebas, pero debe usarse con precaución en hosts compartidos o productivos.
Ejecutar directamente un servicio perfilado
La prueba puede ejecutarse sin activar manualmente test:
docker compose \
-p miapp-dev \
--env-file .env.dev \
-f compose.yaml \
run --rm smoke-testCompose ejecutará smoke-test porque fue seleccionado explícitamente y levantará sus dependencias declaradas. No iniciará adminer ni otros servicios perfilados que no sean necesarios.
Esta característica es especialmente útil para migraciones, tareas administrativas y verificaciones puntuales.
Paso 6: comprobar el resultado
Revisa todos los contenedores, incluidos los detenidos:
docker compose -p miapp-dev ps --allComprueba la aplicación:
curl -fsS http://127.0.0.1:8080/healthConsulta los registros relevantes:
docker compose -p miapp-dev logs --tail=100 app dbComprueba el consumo puntual:
docker stats --no-streamPara acceder a Adminer desde otro equipo, utiliza un túnel SSH en lugar de publicar el puerto en Internet:
ssh -L 8081:127.0.0.1:8081 usuario@TU_IPLuego abre http://127.0.0.1:8081 en el equipo local.
Profiles frente a separación real de entornos
No ejecutes desarrollo y producción con el mismo nombre de proyecto. Compose utiliza ese nombre para construir los nombres de redes, contenedores y volúmenes.
docker compose -p miapp-dev up -d
docker compose -p miapp-stg up -d
docker compose -p miapp-prod up -dEl nombre puede establecerse con -p, COMPOSE_PROJECT_NAME o el atributo superior name. La opción -p tiene mayor precedencia. Nombres de proyecto en Compose.
Una separación consistente requiere:
- Nombre de proyecto distinto.
- Archivos de configuración y variables independientes.
- Redes y volúmenes no compartidos.
- Secretos y credenciales diferentes.
- Puertos de host sin colisiones.
- Permisos y acceso al Docker daemon controlados.
Los perfiles no son control de acceso. Un usuario con permisos sobre Docker puede activar cualquier perfil definido.
Detener un servicio opcional
Para detener y retirar únicamente Adminer:
docker compose -p miapp-dev stop adminer
docker compose -p miapp-dev rm -f adminerNo uses docker compose down -v como rollback habitual: la opción -v elimina los volúmenes declarados y puede destruir datos.
Si una modificación del archivo falla, recupera la versión anterior desde el control de versiones, valida nuevamente con config --quiet y vuelve a ejecutar up -d.
Problemas frecuentes
El servicio perfilado no aparece
Comprueba los perfiles y servicios reconocidos:
docker compose config --profiles
docker compose --profile debug config --servicesRevisa también la indentación YAML y que el nombre del perfil coincida exactamente.
Se inició un servicio aunque su perfil no estaba activo
Puede haber sido seleccionado explícitamente:
docker compose run servicio
docker compose up servicioLa selección directa habilita ese servicio y sus dependencias.
Compose informa una dependencia inválida
Una dependencia perfilada debe estar disponible en el conjunto activo. Mantén los servicios principales sin perfiles y evita que un servicio base dependa de una herramienta opcional.
Desarrollo y producción comparten recursos
Comprueba el nombre del proyecto:
docker compose ls
docker compose -p miapp-dev ps
docker compose -p miapp-prod psRevisa además los nombres de volúmenes y redes. No marques como external un recurso que no deba compartirse.
Una variable no llega al contenedor
--env-file puede estar utilizándose solo para interpolar el archivo Compose. Si la aplicación necesita la variable dentro del contenedor, declárala explícitamente mediante environment, env_file o un secreto, según su sensibilidad.
Buenas prácticas
- Deja los servicios principales sin
profiles. - Usa perfiles para componentes opcionales y tareas puntuales.
- Valida cada combinación relevante con
docker compose config. - Conserva una matriz documentada de perfiles permitidos por entorno.
- Fija imágenes por digest y revisa sus actualizaciones.
- Restringe paneles de administración a localhost o a una red privada.
- Mantén separados datos, redes, credenciales y nombres de proyecto.
- Prueba restauración y rollback sin utilizar datos productivos.