dockerdocker composedevopspostgresql

docker-compose.yml explicado: servicios, volúmenes y redes de tu stack local

Cómo montar un stack completo con Docker Compose: app Node + PostgreSQL conectados por DNS interno, volúmenes nombrados vs bind mounts y políticas restart.

23 de agosto de 2026·7 min de lectura

Docker Compose resuelve el problema que aparece el segundo día de cualquier proyecto: tu aplicación ya no está sola, necesita una base de datos, quizá Redis para caché, y quieres levantar todo con un solo comando. Un docker-compose.yml bien escrito describe ese stack completo y hace que cualquier compañero lo ejecute sin leer documentación.

El stack mínimo real: app + base de datos

Este es el punto de partida del 90% de proyectos backend:

services:
  app:
    image: miapp:latest
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgres://app:cambiar@db:5432/app
    restart: unless-stopped
    depends_on:
      - db
  db:
    image: postgres:16-alpine
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=app
      - POSTGRES_PASSWORD=cambiar
      - POSTGRES_DB=app
    restart: unless-stopped

volumes:
  pgdata:

Con esto, docker compose up -d levanta ambos contenedores conectados.

El detalle que desbloquea todo: DNS interno por nombre de servicio

Mira la cadena de conexión: postgres://app:cambiar@db:5432/app. El host es db — el nombre del servicio en el YAML. Compose crea una red interna donde cada servicio se registra con su nombre como hostname. Tu aplicación no necesita saber IPs ni usar localhost (que dentro del contenedor apunta al propio contenedor): resuelve db directamente.

Esta es la razón por la que los stacks Compose funcionan sin configuración adicional: la red la crea y la puebla Docker solo.

Ojo con depends_on: controla orden de arranque, no disponibilidad. Garantiza que el contenedor de Postgres arranca antes que el de la app, pero Postgres tarda unos segundos en aceptar conexiones tras arrancar. Si tu app crashea al inicio porque la BD aún no responde, necesitas lógica de reintento en la aplicación (o healthchecks con depends_on: condition: service_healthy).

Volúmenes nombrados vs bind mounts

La distinción más importante del archivo entero:

Volumen nombrado (pgdata:/var/lib/postgresql/data): Docker gestiona el almacenamiento en su zona propia. Es lo correcto para datos de bases de datos: rendimiento nativo, portabilidad entre sistemas, y persistencia aunque borres contenedores (solo desaparece con docker compose down -v).

Bind mount (./nginx.conf:/etc/nginx/nginx.conf:ro): mapea una carpeta o fichero de tu máquina al contenedor. Ideal para configuración que editas en vivo y para desarrollo con hot-reload (./src:/app/src). El sufijo :ro lo monta read-only.

Un error clásico es usar bind mount para datos de BD en desarrollo y luego preguntarse por qué en producción (donde las rutas locales no existen) todo se rompe: los volúmenes nombrados son la versión portable.

Puertos: host:contenedor

"3000:3000" significa puerto 3000 de tu máquina → puerto 3000 del contenedor. Pueden diferir: "8080:80" expone un nginx interno en tu puerto 8080. Dos matices:

  1. Si dos servicios publican el mismo puerto del host, el segundo falla al arrancar.
  2. Los puertos entre servicios de la red interna NO necesitan publicarse: tu app accede a Postgres por db:5432 aunque no publiques el 5432. En producción seria, ni siquiera publiques el puerto de la BD — deja que solo sea accesible desde la red interna.

La política restart que quieres

Compose ofrece cuatro valores y uno claramente ganador para servidores:

  • no (por defecto): nunca reinicia. Solo tiene sentido en desarrollo activo.
  • on-failure: reinicia solo si el proceso termina con código de error. Razonable para jobs que pueden fallar puntualmente.
  • always: siempre reinicia, incluso tras un docker stop manual y un reinicio del daemon. Sorprendentemente molesto: apagas el servicio y vuelve.
  • unless-stopped: como always pero respeta una parada manual. Es el valor correcto por defecto para producción: sobrevive a reinicios del servidor y no resucita cuando tú decides pararlo.

Sin clave version:

Si has leído tutoriales antiguos verás una primera línea version: "3.8". Está obsoleta: la especificación actual de Compose la ignoró primero y la eliminó después. Un YAML moderno empieza directamente en services:. Si alguna herramienta te exige la clave, es señal de que está desactualizada.

Variables sensibles fuera del YAML

El ejemplo incluye POSTGRES_PASSWORD=cambiar por brevedad, pero en un repositorio real esas credenciales deben venir del entorno:

environment:
  - POSTGRES_PASSWORD=${DB_PASSWORD}

Compose lee .env del mismo directorio automáticamente. El .env va al .gitignore; el compose, al repo.

Genera tu stack visualmente

Añadir PostgreSQL, MySQL, MariaDB, Redis, MongoDB, Nginx o una app Node con sus presets de imagen, puerto, volumen y variables es cuestión de clics en nuestro generador de Docker Compose: configuras cada servicio, marcas dependencias con depends_on y copias el YAML final.

Preguntas frecuentes

¿Compose sirve para producción? Para VPS pequeños y self-hosting, sí — es una alternativa razonable a Kubernetes cuando este sería matar moscas a cañonazos. Para orquestación multi-nodo con escalado automático, necesitarás algo más.

¿Cómo veo los logs de un servicio? docker compose logs -f app. Con -f sigues el stream en vivo; filtra por nombre de servicio.

¿Puedo ejecutar comandos dentro de un contenedor? docker compose exec db psql -U app abre una consola de Postgres dentro del contenedor db sin instalar nada en tu máquina.


Monta tu stack con presets listos usando el generador de Docker Compose, gratis y sin registro.

Pruébalo sin código

Generador de Docker Compose

Stack local con presets comunes.

Abrir Generador de Docker Compose

Hecho por

Miguel Ángel Colorado Marin (MACM)

Full-Stack Developer · Guadalajara, España

Desarrollo aplicaciones web, herramientas digitales y proyectos completos — desde el diseño hasta el despliegue.

Contáctame