X

Docker Compose en 2026: guía práctica

Una de las herramientas más importantes del desarrollo actual y del despliegue de aplicaciones es Docker, un software que permite empaquetar aplicaciones y sus dependencias dentro de contenedores, lo cual facilita que un mismo software pueda ejecutarse de manera idéntica sin importar el entorno. Aunque en la práctica, son pocas las aplicaciones que están formadas por un solo contenedor. Habitualmente, un sistema puede necesitar un servidor web, una base de datos, un caché Redis, workers para procesar tareas en segundo plano y diferentes servicios auxiliares. Todas esas funciones representan contenedores separados.

Crear, desplegar y administrar cada uno de esos contenedores individualmente con comandos docker run termina siendo muy poco práctico. No solo porque hay que recordar puertos, volúmenes, variables de entorno, redes y dependencias entre servicios, sino que además seguramente hay un orden para hacerlo. De nada sirve levantar el contenedor web si aún no están listos el caché ni la base de datos. Esa es justamente la razón de ser de Docker Compose, el cual nació precisamente para resolver este problema, permitiendo definir toda la infraestructura necesaria para una aplicación dentro de uno o varios archivos de configuración. Por eso, Docker Compose es una de las herramientas más sencillas para administrar aplicaciones conformadas por varios contenedores.

¿Qué es Docker Compose y para qué sirve?

Docker Compose es una herramienta que permite definir en formato de texto y de forma estructurada cómo una aplicación formada por múltiples contenedores será desplegada. En lugar de ejecutar cada contenedor por separado, toda la configuración puede describirse explícitamente dentro de un archivo compose.yaml.

En este archivo se pueden definir los servicios que forman parte de la aplicación, las imágenes de Docker que utiliza cada uno, los puertos publicados, los volúmenes de almacenamiento, las variables de entorno, las redes internas y básicamente cualquier componente necesario para ejecutar el entorno.

Por ejemplo, una aplicación web podría tener un servicio web basado en Nginx, otro denominado app con PHP y uno llamado database ejecutando MariaDB, PostgreSQL o cualquier base de datos que se necesite. Entonces Compose interpreta esta configuración y crea los contenedores, redes y volúmenes necesarios para ejecutar la aplicación.

La principal ventaja de Compose es que la infraestructura y el despliegue pasan a estar definidos en archivos en forma de configuración reproducible, es decir, que se puede desplegar el sistema exactamente igual en cualquier entorno. En lugar de tener un documento con una larga secuencia de comandos para levantar un entorno manualmente, basta con almacenar el archivo Compose junto al proyecto y ejecutarlo donde sea necesario. Lo que permite pasar desde el PC del desarrollador, por el entorno de testing y llegar hasta producción con la garantía de que se puede reproducir sin problemas de compatibilidad por dependencias externas.

Compose puede ser utilizado para aplicaciones pequeñas, servidores internos, laboratorios, herramientas de desarrollo, servicios caseros o determinados despliegues profesionales de producción donde no sea necesario utilizar una plataforma de orquestación ordenada.

Del antiguo docker-compose a docker compose

Durante muchos años, Docker Compose fue una aplicación independiente escrita en Python. De hecho, originalmente ni siquiera formaba parte oficial de Docker, sino que nació bajo el nombre de Fig, una herramienta desarrollada por un tercero cuya empresa terminó siendo comprada por Docker en 2014. Compose V1 utilizaba un ejecutable independiente llamado «docker-compose». Esto cambió con Compose V2, reescrito en Go y presentado en 2021, que pasó a integrarse con la CLI de Docker como un plugin y a ejecutarse mediante el comando «docker compose».

La historia de las versiones puede resultar algo confusa porque durante años también existieron versiones del formato del archivo Compose, como 2.x y 3.x. Estas versiones terminaron fusionándose dentro de la actual Compose Specification.

Por esta razón, tampoco es necesario comenzar los archivos actuales con declaraciones como versión: «3.8». La propiedad versión todavía es reconocida por compatibilidad, pero Docker la considera obsoleta y Compose utiliza siempre el esquema actual para interpretar el archivo.

Cómo funciona un archivo compose.yaml

El archivo principal de un proyecto se denomina habitualmente compose.yaml. Docker también reconoce nombres históricos como docker-compose.yaml, aunque compose.yaml representa actualmente la convención de nombre moderna. La estructura básica comienza normalmente con la propiedad services. Dentro se declara cada componente de la aplicación y se asigna un nombre lógico.

Un ejemplo sencillo podría tener el siguiente aspecto:

services:
  web:
    image: php:8.3-apache
    ports:
      - "8080:80"
    volumes:
      - ./html:/var/www/html

  database:
    image: mariadb:11
    environment:
      MARIADB_ROOT_PASSWORD: example

En el ejemplo anterior se muestra de forma simple cómo levantar dos servicios de forma sencilla. El primero ejecuta un Apache con PHP y expone el puerto 80 del contenedor mediante el puerto 8080 del host. El segundo crea una instancia de la base de datos MariaDB y configura una variable de entorno.

Cuando se ejecuta docker compose up, Compose revisa el archivo y compara la configuración que en él se declara con lo que hay creado, si lo hay. Si los contenedores todavía no existen, los crea. Si la configuración de un servicio ha cambiado, puede rehacerlo.

La creación de los contenedores se realiza mediante imágenes que se pueden descargar desde el repositorio oficial (u otro repositorio privado) o directamente compilar una imagen nueva desde cero usando un archivo Dockerfile y luego en el YAML declarar build:

services:
  app:
    build: .
    ports:
      - "8000:80"

Esta separación resulta importante como concepto porque mientras el Dockerfile describe cómo construir una imagen, el compose.yaml describe cómo ejecutar y relacionar los servicios que forman parte de la aplicación.

Servicios, puertos, variables y volúmenes

Los servicios son la pieza más importante en Compose porque cada servicio representa normalmente una función concreta dentro de la aplicación y genera uno o varios contenedores basados en la configuración que se declara en el archivo YAML.

La propiedad image indica qué imagen utilizar. Puede referirse a imágenes públicas de Docker Hub, imágenes almacenadas en otro repositorio o imágenes creadas localmente. Cuando el servicio debe construirse a partir del código del proyecto, se utiliza build.

Los puertos permiten exponer servicios fuera de las redes internas de Docker. Una configuración como «8080:80» publica el puerto 80 del contenedor en el puerto 8080 del host. Esto permite acceder al servicio mediante localhost:8080 o mediante la dirección correspondiente del servidor.

No todos los servicios necesitan publicar puertos. Una base de datos utilizada exclusivamente por otros contenedores normalmente puede permanecer accesible únicamente dentro de la red de Compose. De esta manera, no es necesario exponer MySQL, PostgreSQL o Redis directamente en el host.

Las variables de entorno permiten proporcionar configuración a los contenedores. Pueden declararse directamente mediante environment, cargarse utilizando env_file o construirse utilizando interpolación.

Por ejemplo, un archivo .env podría contener:

APP_PORT=8080
DB_PASSWORD=secret

El archivo compose.yaml puede utilizar estos valores:

services:
  web:
    image: example/web
    ports:
      - "${APP_PORT}:80"

  database:
    image: mariadb:11
    environment:
      MARIADB_ROOT_PASSWORD: ${DB_PASSWORD}

Compose permite utilizar expresiones como ${VARIABLE} para que, además de valores fijos, se puedan configurar valores que provengan de variables de entorno. El comando docker compose config resulta especialmente útil para comprobar cuál será finalmente la configuración después de resolver estas variables.

Los volúmenes son una opción que resuelve un problema frecuente de los contenedores: la persistencia de datos. El concepto básico de contenedor es que sea un elemento descartable, por lo que un contenedor puede eliminarse y volver a crearse en cualquier momento, pero la información importante no debería estar en el contenedor. Allí es donde entran los volúmenes, una especie de disco virtual que se monta dentro del contenedor en las rutas en las que se necesite guardar información que debe sobrevivir a la destrucción del contenedor.

Una base de datos puede utilizar un volumen nombrado:

services:
  database:
    image: mariadb:11
    volumes:
      - database_data:/var/lib/mysql

volumes:
  database_data:

También pueden utilizarse bind mounts para conectar archivos o directorios del host directamente con el contenedor, lo cual es habitual durante el desarrollo cuando se quiere que las modificaciones realizadas sobre el código fuente estén inmediatamente disponibles dentro del contenedor.

Networks y comunicación entre contenedores

Una de las funciones más cómodas que brinda Docker Compose es que no es necesario configurar manualmente la red entre los diferentes servicios. Cuando un proyecto no declara ninguna red, Compose crea automáticamente una red por defecto y conecta los servicios a ella. Los contenedores pueden localizarse entre sí gracias al DNS interno de Docker utilizando los nombres que se le hayan dado a cada servicio.

Por ejemplo, si se tienen los servicios llamados app y database, la aplicación no necesita conocer la dirección IP asignada al contenedor de la base de datos y simplemente se puede utilizar database como hostname.

DB_HOST=database

Esto es preferible a utilizar direcciones IP porque los contenedores pueden ser eliminados y recreados, provocando que cambien sus direcciones internas continuamente. Cuando una arquitectura necesita mayor aislamiento, también pueden declararse nuevas redes:

services:
  proxy:
    image: nginx
    networks:
      - frontend

  app:
    image: example/app
    networks:
      - frontend
      - backend

  database:
    image: mariadb:11
    networks:
      - backend

networks:
  frontend:
  backend:

En este diseño, el proxy puede comunicarse con la app mediante el frontend, mientras que la app puede comunicarse con la base de datos mediante el backend. El proxy y la base de datos no comparten ninguna red, por lo que no pueden comunicarse directamente. Este mecanismo permite mostrar de forma sencilla diferentes capas de una aplicación y reducir conexiones innecesarias entre servicios.

Comandos esenciales para administrar un stack

Composer brinda muchos comandos y entre los más importantes que se deben conocer para trabajar de buena manera están los siguientes:

Comando Función
docker compose up Crea los recursos necesarios e inicia los servicios del proyecto, mostrando sus logs en la terminal.
docker compose up -d Inicia los servicios en segundo plano.
docker compose up -d –build Construye las imágenes antes de iniciar los servicios en segundo plano.
docker compose ps Muestra los contenedores en ejecución del proyecto, su estado y sus puertos.
docker compose ps -a Muestra también los contenedores detenidos del proyecto.
docker compose logs Muestra los logs de los servicios.
docker compose logs -f Sigue los logs y muestra los nuevos mensajes a medida que se generan.
docker compose logs -f database Sigue únicamente los logs del servicio database.
docker compose exec app bash Abre una shell Bash en el contenedor en ejecución del servicio app, si tiene Bash instalado.
docker compose run –rm app <command> Crea un contenedor del servicio app para ejecutar una tarea puntual y lo elimina al terminar. Sustituye <command> por el comando deseado.
docker compose down Detiene y elimina los contenedores y las redes del proyecto, conservando los volúmenes persistentes.
docker compose pull Descarga del registro las imágenes de los servicios sin iniciarlos.
docker compose config Valida la configuración, sustituye las variables por sus valores, combina los archivos Compose utilizados y muestra el resultado final.

Buenas prácticas y errores comunes

Ejecutar el servidor web, la base de datos, Redis y cualquier otro proceso dentro del mismo contenedor elimina buena parte de las ventajas que proporciona la arquitectura basada en contenedores, como la separación de responsabilidades. Dificulta el monitoreo y el diagnóstico de problemas y errores. Por eso, una buena configuración de Compose debería intentar mantener cada contenedor con una única responsabilidad.

También es indispensable tener presente qué información debe ser persistente y qué información debe ser descartable. Datos como las bases de datos, archivos subidos por usuarios y cualquier otra información que deba sobrevivir a la eliminación y reconstrucción de un contenedor, por tanto, deberían almacenarse mediante volúmenes o algún sistema externo de almacenamiento. Es un error común olvidar establecer que los datos de la base de datos queden fuera del contenedor o en un volumen y que todos los datos se pierdan tras tener que reconstruir el contenedor.

Otro error habitual consiste en publicar todos los puertos simplemente porque un servicio los utiliza. Sin embargo, esto no es necesario. Los contenedores pertenecientes a una misma red de Compose pueden comunicarse directamente sin publicar esos puertos en el host. Una base de datos que solamente utiliza la aplicación puede escuchar internamente en el puerto 3306 sin necesidad de exponerlo públicamente.

Las imágenes también deberían utilizar versiones específicas, dado que depender sistemáticamente de etiquetas genéricas como latest puede hacer que dos despliegues que se supone que deben ser idénticos terminen ejecutando versiones diferentes. Resulta más seguro definir explícitamente las versiones utilizadas de forma fija y actualizarlas en el archivo de Docker Compose cuando realmente se pretenda cambiar de versión.

También resulta recomendable utilizar nombres de servicio para la comunicación interna en lugar de depender de direcciones IP. Compose proporciona un servicio de descubrimiento de servicios precisamente para evitar que la aplicación dependa de direcciones que pueden cambiar cuando los contenedores son recreados.

Finalmente, antes de aplicar una configuración, conviene ejecutar docker compose config. Este comando comprueba si hay errores y muestra cómo queda la configuración con los valores de las variables y los cambios de los distintos archivos de Compose. Así puedes comprobar que todo esté como esperas antes de iniciar los contenedores.

Conclusión

Docker Compose permite administrar una aplicación formada por varios servicios sin tener que crear y configurar cada contenedor manualmente. Al definir los servicios, las redes, los volúmenes y las variables de entorno dentro de un archivo, resulta más sencillo mantener la configuración junto al proyecto y repetir el despliegue donde sea necesario. Esto reduce el trabajo manual y facilita tanto el desarrollo como la administración de la aplicación.

Sin embargo, tener toda la configuración en un archivo no elimina la necesidad de entender cómo funciona cada servicio y qué necesita para ejecutarse correctamente. Definir dónde se guardan los datos, qué puertos deben publicarse y cómo se comunican los contenedores sigue siendo parte de la administración del sistema. Con estos conceptos claros, Compose permite organizar y administrar con unos pocos comandos, sin llegar a necesitar una plataforma de orquestación más compleja.

Artículos relacionados