{"id":1282,"date":"2026-03-12T14:20:12","date_gmt":"2026-03-12T17:20:12","guid":{"rendered":"https:\/\/maurobernal.com.ar\/blog\/?p=1282"},"modified":"2026-03-12T14:20:12","modified_gmt":"2026-03-12T17:20:12","slug":"docker-compose-de-cero-a-produccion-parte-3","status":"publish","type":"post","link":"https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/","title":{"rendered":"Docker Compose de cero a producci\u00f3n \u2014 Parte 3: Proyectos reales y estrategias Dev vs Prod"},"content":{"rendered":"\n<p>Llegamos al cierre de la serie <strong>Docker Compose de cero a producci\u00f3n<\/strong>. En la <a href=\"\/docker-compose-de-cero-a-produccion-parte-1\">Parte 1<\/a> vimos el archivo compose.yaml, en la <a href=\"\/docker-compose-de-cero-a-produccion-parte-2\">Parte 2<\/a> los comandos y las dependencias robustas. Ahora lo llevamos a la pr\u00e1ctica: dos proyectos reales completos y la estrategia que uso para manejar configuraciones distintas entre desarrollo y producci\u00f3n.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Proyecto 1: WordPress + MySQL<\/h2>\n\n\n\n<p>El ejemplo cl\u00e1sico de un stack de dos niveles: una aplicaci\u00f3n web y su base de datos. Este proyecto completo vive en un solo archivo.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Estructura del proyecto<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">wordpress_project\/\n\u2514\u2500\u2500 compose.yaml<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">compose.yaml<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\">services:\n  db:\n    image: mysql:8.0\n    container_name: wordpress_db\n    restart: unless-stopped\n    environment:\n      MYSQL_DATABASE: wordpress\n      MYSQL_USER: wp_user\n      MYSQL_PASSWORD: wp_password\n      MYSQL_ROOT_PASSWORD: root_secret_password\n    volumes:\n      - db_data:\/var\/lib\/mysql\n    networks:\n      - app_network\n    healthcheck:\n      test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\"]\n      interval: 10s\n      timeout: 5s\n      retries: 5\n      start_period: 30s\n\n  wordpress:\n    image: wordpress:latest\n    container_name: wordpress_app\n    restart: unless-stopped\n    depends_on:\n      db:\n        condition: service_healthy   # Espera que MySQL est\u00e9 listo de verdad\n    ports:\n      - \"8000:80\"\n    environment:\n      WORDPRESS_DB_HOST: db:3306     # 'db' es el nombre del servicio = hostname\n      WORDPRESS_DB_USER: wp_user\n      WORDPRESS_DB_PASSWORD: wp_password\n      WORDPRESS_DB_NAME: wordpress\n    networks:\n      - app_network\n\nnetworks:\n  app_network:\n    driver: bridge\n\nvolumes:\n  db_data:\n    driver: local<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Puesta en marcha<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">cd wordpress_project\ndocker compose up -d\n\n# Esperar unos segundos y visitar http:\/\/localhost:8000\n# El instalador de WordPress estar\u00e1 listo<\/code><\/pre>\n\n\n\n<p>Lo que me parece elegante de este ejemplo: <code>WORDPRESS_DB_HOST: db:3306<\/code>. No hay IP, no hay configuraci\u00f3n de DNS manual. El nombre del servicio <code>db<\/code> funciona directamente como hostname gracias a la red que Compose crea autom\u00e1ticamente. Esto es lo que hace que los entornos sean tan portables.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Proyecto 2: API REST con Node.js y PostgreSQL<\/h2>\n\n\n\n<p>Este ejemplo es m\u00e1s representativo del trabajo diario: construimos nuestra propia imagen desde un Dockerfile y configuramos un entorno de desarrollo con hot-reload.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Estructura del proyecto<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">api_project\/\n\u251c\u2500\u2500 compose.yaml\n\u251c\u2500\u2500 Dockerfile\n\u251c\u2500\u2500 index.js\n\u2514\u2500\u2500 package.json<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Dockerfile<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"dockerfile\" class=\"language-dockerfile\">FROM node:18-alpine\nWORKDIR \/usr\/src\/app\nCOPY package*.json .\/\nRUN npm install\nCOPY . .\nEXPOSE 3000\nCMD [\"npm\", \"start\"]<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">index.js<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">const express = require('express');\nconst { Pool } = require('pg');\nconst app = express();\n\nconst pool = new Pool({\n  user: process.env.DB_USER,\n  host: process.env.DB_HOST,      \/\/ Viene del ambiente: 'db'\n  database: process.env.DB_NAME,\n  password: process.env.DB_PASSWORD,\n  port: 5432,\n});\n\napp.get('\/', async (req, res) => {\n  try {\n    const result = await pool.query('SELECT NOW()');\n    res.send(`Hora en la base de datos: ${result.rows[0].now}`);\n  } catch (err) {\n    res.status(500).send('Error conectando a la base de datos');\n  }\n});\n\napp.listen(3000, () => console.log('API corriendo en http:\/\/localhost:3000'));<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">compose.yaml<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\">services:\n  db:\n    image: postgres:15-alpine\n    environment:\n      POSTGRES_DB: apidb\n      POSTGRES_USER: apiuser\n      POSTGRES_PASSWORD: apipassword\n    volumes:\n      - pg_data:\/var\/lib\/postgresql\/data\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U apiuser -d apidb\"]\n      interval: 10s\n      timeout: 5s\n      retries: 5\n\n  api:\n    build: .\n    ports:\n      - \"3000:3000\"\n    volumes:\n      - .:\/usr\/src\/app       # Bind mount: cambios en el c\u00f3digo se reflejan al instante\n    environment:\n      DB_HOST: db\n      DB_USER: apiuser\n      DB_PASSWORD: apipassword\n      DB_NAME: apidb\n    depends_on:\n      db:\n        condition: service_healthy\n\nvolumes:\n  pg_data: {}<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\"># La primera vez, --build para construir la imagen\ndocker compose up --build -d\n\n# Verificar que funciona\ncurl http:\/\/localhost:3000\n# \u2192 Hora en la base de datos: 2025-05-01 14:23:45.123456+00<\/code><\/pre>\n\n\n\n<p>El bind mount (<code>- .:\/usr\/src\/app<\/code>) es el truco de desarrollo: cualquier cambio en el c\u00f3digo se refleja dentro del contenedor sin reconstruir la imagen. Para que los cambios se apliquen autom\u00e1ticamente en Node.js, agreg\u00e1 <code>nodemon<\/code> como dependencia de desarrollo y usalo en el CMD.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Gesti\u00f3n de entornos: desarrollo vs producci\u00f3n<\/h2>\n\n\n\n<p>Esta fue una de las lecciones m\u00e1s importantes que aprend\u00ed: <strong>nunca uses el mismo archivo compose.yaml sin modificaciones para desarrollo y producci\u00f3n<\/strong>. Las necesidades son opuestas: en desarrollo quer\u00e9s agilidad y debuggear f\u00e1cil; en producci\u00f3n, estabilidad, seguridad y recursos bien definidos.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Estrategia con compose.override.yaml<\/h3>\n\n\n\n<p>Compose tiene un mecanismo autom\u00e1tico: si existe un <code>compose.override.yaml<\/code> en el mismo directorio, lo fusiona autom\u00e1ticamente con el <code>compose.yaml<\/code> base al ejecutar <code>docker compose up<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\"># compose.yaml \u2014 Base (versionado en Git)\nservices:\n  api:\n    build: .\n    restart: unless-stopped\n    environment:\n      - APP_ENV=production<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\"># compose.override.yaml \u2014 Desarrollo (puede NO versionarse si tiene configs locales)\nservices:\n  api:\n    ports:\n      - \"8000:8000\"\n      - \"9229:9229\"        # Puerto de debugging de Node.js\n    volumes:\n      - .:\/app             # Bind mount para hot-reload\n    environment:\n      - APP_ENV=development   # Sobreescribe el valor del base\n\n  # Servicio adicional solo para desarrollo\n  pgadmin:\n    image: dpage\/pgadmin4\n    ports:\n      - \"5050:80\"\n    environment:\n      PGADMIN_DEFAULT_EMAIL: dev@local.com\n      PGADMIN_DEFAULT_PASSWORD: dev<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Estrategia con m\u00faltiples archivos expl\u00edcitos<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\"># compose.prod.yaml \u2014 Producci\u00f3n\nservices:\n  api:\n    restart: always\n    deploy:\n      resources:\n        limits:\n          cpus: '1.0'\n          memory: 512M\n    logging:\n      driver: \"json-file\"\n      options:\n        max-size: \"10m\"\n        max-file: \"3\"<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\"># Desarrollo (usa override autom\u00e1tico)\ndocker compose up -d\n\n# Producci\u00f3n (combina base + prod expl\u00edcitamente)\ndocker compose -f compose.yaml -f compose.prod.yaml up -d<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Archivos .env para variables de entorno<\/h3>\n\n\n\n<p>Para evitar hardcodear valores en los archivos YAML (especialmente secretos), uso archivos <code>.env<\/code>. Compose los carga autom\u00e1ticamente si existen en el mismo directorio.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\"># .env (NO versionar si tiene contrase\u00f1as reales \u2014 agregarlo al .gitignore)\nTAG=15-alpine\nPOSTGRES_USER=miusuario\nPOSTGRES_PASSWORD=supersecreto\nPOSTGRES_DB=miapp<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\"># compose.yaml usando las variables del .env\nservices:\n  db:\n    image: postgres:${TAG}\n    environment:\n      POSTGRES_USER: ${POSTGRES_USER}\n      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}\n      POSTGRES_DB: ${POSTGRES_DB}<\/code><\/pre>\n\n\n\n<p>Cada desarrollador tiene su propio <code>.env<\/code> local con sus credenciales. El archivo <code>compose.yaml<\/code> se versiona sin datos sensibles. Para producci\u00f3n, las variables se inyectan desde el CI\/CD o desde un gestor de secretos.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Builds multi-stage para producci\u00f3n<\/h3>\n\n\n\n<p>Las im\u00e1genes de desarrollo son gordas (incluyen herramientas de build, dependencias de dev, etc.). Las de producci\u00f3n deben ser m\u00ednimas. Los builds multi-stage resuelven esto con un solo Dockerfile:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"dockerfile\" class=\"language-dockerfile\"># Dockerfile\n# ---- Etapa de Build ----\nFROM node:20-alpine AS builder\nWORKDIR \/app\nCOPY package.json .\nRUN npm install\nCOPY . .\nRUN npm run build\n\n# ---- Etapa de Producci\u00f3n ----\nFROM node:20-slim AS production\nWORKDIR \/app\nCOPY --from=builder \/app\/dist .\/dist\nCOPY --from=builder \/app\/node_modules .\/node_modules\nEXPOSE 3000\nCMD [\"node\", \"dist\/index.js\"]<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\"># compose.yaml\nservices:\n  api:\n    build:\n      context: .\n      target: production   # Solo construye hasta la etapa 'production'<\/code><\/pre>\n\n\n\n<p>La imagen de producci\u00f3n solo contiene lo necesario para correr \u2014 sin el compilador, sin herramientas de build, sin dependencias de desarrollo. M\u00e1s peque\u00f1a, m\u00e1s segura, m\u00e1s r\u00e1pida de desplegar.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">El checklist final para ir a producci\u00f3n con Compose<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>&#x2705; <code>restart: unless-stopped<\/code> o <code>always<\/code> en todos los servicios cr\u00edticos<\/li>\n<li>&#x2705; Healthchecks definidos en las bases de datos y servicios de los que dependen otros<\/li>\n<li>&#x2705; <code>depends_on<\/code> con <code>condition: service_healthy<\/code> donde corresponde<\/li>\n<li>&#x2705; Vol\u00famenes nombrados para cualquier dato que deba persistir<\/li>\n<li>&#x2705; Secretos y contrase\u00f1as en <code>.env<\/code> o variables de CI\/CD, nunca hardcodeados<\/li>\n<li>&#x2705; Im\u00e1genes multi-stage para producci\u00f3n<\/li>\n<li>&#x2705; <code>deploy.resources.limits<\/code> configurados para evitar que un servicio consuma todo el host<\/li>\n<li>&#x2705; Logging configurado (<code>json-file<\/code> con <code>max-size<\/code> para evitar llenar el disco)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\u00bfY despu\u00e9s de Compose?<\/h2>\n\n\n\n<p>Docker Compose es ideal para un \u00fanico host. Cuando necesit\u00e1s escalar a m\u00faltiples m\u00e1quinas, alta disponibilidad real o gesti\u00f3n de carga compleja, los pr\u00f3ximos pasos naturales son:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Docker Swarm:<\/strong> orquestaci\u00f3n multi-host con conceptos muy similares a Compose. Curva de aprendizaje baja si ya domin\u00e1s Compose.<\/li>\n<li><strong>Kubernetes:<\/strong> el est\u00e1ndar de la industria para producci\u00f3n a gran escala. La herramienta <code>kompose convert<\/code> traduce tus archivos Compose a manifiestos de Kubernetes para facilitar la migraci\u00f3n.<\/li>\n<\/ul>\n\n\n\n<p>Lo bueno: los conceptos que aprendiste en esta serie (servicios, redes, vol\u00famenes, variables de entorno, healthchecks) se trasladan directamente a esos entornos.<\/p>\n\n\n\n<p><em>\u2190 <a href=\"\/docker-compose-de-cero-a-produccion-parte-2\">Parte 2: CLI esencial, depends_on, healthchecks y perfiles<\/a><\/em><\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>Dos proyectos completos (WordPress + MySQL, Node.js + PostgreSQL) y la estrategia con compose.override.yaml, m\u00faltiples archivos, .env y builds multi-stage para manejar desarrollo y producci\u00f3n desde el mismo base.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1,233],"tags":[254,257,263,328,241,242,240,259,248,261,327,330,238,266,326,250,256,333,271,335,331,255,334,329,262,270,268,272,244,253,332,325],"class_list":["post-1282","post","type-post","status-publish","format-standard","hentry","category-blog","category-kubernetes","tag-bind-mount","tag-bridge","tag-ci-cd","tag-compose-override","tag-contenedores","tag-devops","tag-docker","tag-docker-compose","tag-dockerfile","tag-entornos","tag-env-file","tag-escalado","tag-kubernetes","tag-microservicios","tag-multi-container","tag-multistage","tag-networking","tag-nodejs-docker","tag-orquestacion","tag-orquestacion-local","tag-perfiles","tag-persistencia","tag-postgresql-docker","tag-produccion","tag-reproducibilidad","tag-secrets","tag-seguridad","tag-swarm","tag-virtualizacion","tag-volumenes","tag-wordpress-docker","tag-yaml"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Docker Compose de cero a producci\u00f3n \u2014 Parte 3: Proyectos reales y estrategias Dev vs Prod &#183; devops Mauro Bernal<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Docker Compose de cero a producci\u00f3n \u2014 Parte 3: Proyectos reales y estrategias Dev vs Prod &#183; devops Mauro Bernal\" \/>\n<meta property=\"og:description\" content=\"Dos proyectos completos (WordPress + MySQL, Node.js + PostgreSQL) y la estrategia con compose.override.yaml, m\u00faltiples archivos, .env y builds multi-stage para manejar desarrollo y producci\u00f3n desde el mismo base.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/\" \/>\n<meta property=\"og:site_name\" content=\"devops Mauro Bernal\" \/>\n<meta property=\"article:published_time\" content=\"2026-03-12T17:20:12+00:00\" \/>\n<meta name=\"author\" content=\"Mauro Bernal\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@_maurobernal\" \/>\n<meta name=\"twitter:site\" content=\"@_maurobernal\" \/>\n<meta name=\"twitter:label1\" content=\"Escrito por\" \/>\n\t<meta name=\"twitter:data1\" content=\"Mauro Bernal\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tiempo de lectura\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/docker-compose-de-cero-a-produccion-parte-3\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/docker-compose-de-cero-a-produccion-parte-3\\\/\"},\"author\":{\"name\":\"Mauro Bernal\",\"@id\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/#\\\/schema\\\/person\\\/09c4dbdfb59b20e015c703fd19713283\"},\"headline\":\"Docker Compose de cero a producci\u00f3n \u2014 Parte 3: Proyectos reales y estrategias Dev vs Prod\",\"datePublished\":\"2026-03-12T17:20:12+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/docker-compose-de-cero-a-produccion-parte-3\\\/\"},\"wordCount\":664,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/#\\\/schema\\\/person\\\/09c4dbdfb59b20e015c703fd19713283\"},\"keywords\":[\"bind-mount\",\"bridge\",\"ci-cd\",\"compose-override\",\"contenedores\",\"devops\",\"docker\",\"docker-compose\",\"dockerfile\",\"entornos\",\"env-file\",\"escalado\",\"kubernetes\",\"microservicios\",\"multi-container\",\"multistage\",\"networking\",\"nodejs-docker\",\"orquestacion\",\"orquestacion-local\",\"perfiles\",\"persistencia\",\"postgresql-docker\",\"produccion\",\"reproducibilidad\",\"secrets\",\"seguridad\",\"swarm\",\"virtualizaci\u00f3n\",\"volumenes\",\"wordpress-docker\",\"yaml\"],\"articleSection\":[\"Blog\",\"Kubernetes\"],\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/docker-compose-de-cero-a-produccion-parte-3\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/docker-compose-de-cero-a-produccion-parte-3\\\/\",\"url\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/docker-compose-de-cero-a-produccion-parte-3\\\/\",\"name\":\"Docker Compose de cero a producci\u00f3n \u2014 Parte 3: Proyectos reales y estrategias Dev vs Prod &#183; devops Mauro Bernal\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/#website\"},\"datePublished\":\"2026-03-12T17:20:12+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/docker-compose-de-cero-a-produccion-parte-3\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/docker-compose-de-cero-a-produccion-parte-3\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/docker-compose-de-cero-a-produccion-parte-3\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Portada\",\"item\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Docker Compose de cero a producci\u00f3n \u2014 Parte 3: Proyectos reales y estrategias Dev vs Prod\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/\",\"name\":\"devops Mauro Bernal\",\"description\":\"Cuando tu trabajo es hacer que las cosas funcionen bien...\",\"publisher\":{\"@id\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/#\\\/schema\\\/person\\\/09c4dbdfb59b20e015c703fd19713283\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/maurobernal.com.ar\\\/blog\\\/#\\\/schema\\\/person\\\/09c4dbdfb59b20e015c703fd19713283\",\"name\":\"Mauro Bernal\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/i0.wp.com\\\/maurobernal.com.ar\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/07\\\/logo-maurobernal.png?fit=1740%2C1740&ssl=1\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/maurobernal.com.ar\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/07\\\/logo-maurobernal.png?fit=1740%2C1740&ssl=1\",\"contentUrl\":\"https:\\\/\\\/i0.wp.com\\\/maurobernal.com.ar\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/07\\\/logo-maurobernal.png?fit=1740%2C1740&ssl=1\",\"width\":1740,\"height\":1740,\"caption\":\"Mauro Bernal\"},\"logo\":{\"@id\":\"https:\\\/\\\/i0.wp.com\\\/maurobernal.com.ar\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/07\\\/logo-maurobernal.png?fit=1740%2C1740&ssl=1\"},\"description\":\"Desarrollo de Sistemas en .Net, IT Callcenters, DBA de SQL Server, Mikrotik, Pentest y T\u00e9cnico consultor de Sistemas Bejerman\",\"sameAs\":[\"https:\\\/\\\/maurobernal.com.ar\",\"https:\\\/\\\/x.com\\\/_maurobernal\",\"https:\\\/\\\/youtube.com\\\/maurobernal\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Docker Compose de cero a producci\u00f3n \u2014 Parte 3: Proyectos reales y estrategias Dev vs Prod &#183; devops Mauro Bernal","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/","og_locale":"es_ES","og_type":"article","og_title":"Docker Compose de cero a producci\u00f3n \u2014 Parte 3: Proyectos reales y estrategias Dev vs Prod &#183; devops Mauro Bernal","og_description":"Dos proyectos completos (WordPress + MySQL, Node.js + PostgreSQL) y la estrategia con compose.override.yaml, m\u00faltiples archivos, .env y builds multi-stage para manejar desarrollo y producci\u00f3n desde el mismo base.","og_url":"https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/","og_site_name":"devops Mauro Bernal","article_published_time":"2026-03-12T17:20:12+00:00","author":"Mauro Bernal","twitter_card":"summary_large_image","twitter_creator":"@_maurobernal","twitter_site":"@_maurobernal","twitter_misc":{"Escrito por":"Mauro Bernal","Tiempo de lectura":"6 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/#article","isPartOf":{"@id":"https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/"},"author":{"name":"Mauro Bernal","@id":"https:\/\/maurobernal.com.ar\/blog\/#\/schema\/person\/09c4dbdfb59b20e015c703fd19713283"},"headline":"Docker Compose de cero a producci\u00f3n \u2014 Parte 3: Proyectos reales y estrategias Dev vs Prod","datePublished":"2026-03-12T17:20:12+00:00","mainEntityOfPage":{"@id":"https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/"},"wordCount":664,"commentCount":0,"publisher":{"@id":"https:\/\/maurobernal.com.ar\/blog\/#\/schema\/person\/09c4dbdfb59b20e015c703fd19713283"},"keywords":["bind-mount","bridge","ci-cd","compose-override","contenedores","devops","docker","docker-compose","dockerfile","entornos","env-file","escalado","kubernetes","microservicios","multi-container","multistage","networking","nodejs-docker","orquestacion","orquestacion-local","perfiles","persistencia","postgresql-docker","produccion","reproducibilidad","secrets","seguridad","swarm","virtualizaci\u00f3n","volumenes","wordpress-docker","yaml"],"articleSection":["Blog","Kubernetes"],"inLanguage":"es","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/","url":"https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/","name":"Docker Compose de cero a producci\u00f3n \u2014 Parte 3: Proyectos reales y estrategias Dev vs Prod &#183; devops Mauro Bernal","isPartOf":{"@id":"https:\/\/maurobernal.com.ar\/blog\/#website"},"datePublished":"2026-03-12T17:20:12+00:00","breadcrumb":{"@id":"https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/maurobernal.com.ar\/blog\/docker-compose-de-cero-a-produccion-parte-3\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Portada","item":"https:\/\/maurobernal.com.ar\/blog\/"},{"@type":"ListItem","position":2,"name":"Docker Compose de cero a producci\u00f3n \u2014 Parte 3: Proyectos reales y estrategias Dev vs Prod"}]},{"@type":"WebSite","@id":"https:\/\/maurobernal.com.ar\/blog\/#website","url":"https:\/\/maurobernal.com.ar\/blog\/","name":"devops Mauro Bernal","description":"Cuando tu trabajo es hacer que las cosas funcionen bien...","publisher":{"@id":"https:\/\/maurobernal.com.ar\/blog\/#\/schema\/person\/09c4dbdfb59b20e015c703fd19713283"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/maurobernal.com.ar\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":["Person","Organization"],"@id":"https:\/\/maurobernal.com.ar\/blog\/#\/schema\/person\/09c4dbdfb59b20e015c703fd19713283","name":"Mauro Bernal","image":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/i0.wp.com\/maurobernal.com.ar\/blog\/wp-content\/uploads\/2023\/07\/logo-maurobernal.png?fit=1740%2C1740&ssl=1","url":"https:\/\/i0.wp.com\/maurobernal.com.ar\/blog\/wp-content\/uploads\/2023\/07\/logo-maurobernal.png?fit=1740%2C1740&ssl=1","contentUrl":"https:\/\/i0.wp.com\/maurobernal.com.ar\/blog\/wp-content\/uploads\/2023\/07\/logo-maurobernal.png?fit=1740%2C1740&ssl=1","width":1740,"height":1740,"caption":"Mauro Bernal"},"logo":{"@id":"https:\/\/i0.wp.com\/maurobernal.com.ar\/blog\/wp-content\/uploads\/2023\/07\/logo-maurobernal.png?fit=1740%2C1740&ssl=1"},"description":"Desarrollo de Sistemas en .Net, IT Callcenters, DBA de SQL Server, Mikrotik, Pentest y T\u00e9cnico consultor de Sistemas Bejerman","sameAs":["https:\/\/maurobernal.com.ar","https:\/\/x.com\/_maurobernal","https:\/\/youtube.com\/maurobernal"]}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack-related-posts":[],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/maurobernal.com.ar\/blog\/wp-json\/wp\/v2\/posts\/1282","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/maurobernal.com.ar\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/maurobernal.com.ar\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/maurobernal.com.ar\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/maurobernal.com.ar\/blog\/wp-json\/wp\/v2\/comments?post=1282"}],"version-history":[{"count":1,"href":"https:\/\/maurobernal.com.ar\/blog\/wp-json\/wp\/v2\/posts\/1282\/revisions"}],"predecessor-version":[{"id":1285,"href":"https:\/\/maurobernal.com.ar\/blog\/wp-json\/wp\/v2\/posts\/1282\/revisions\/1285"}],"wp:attachment":[{"href":"https:\/\/maurobernal.com.ar\/blog\/wp-json\/wp\/v2\/media?parent=1282"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/maurobernal.com.ar\/blog\/wp-json\/wp\/v2\/categories?post=1282"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/maurobernal.com.ar\/blog\/wp-json\/wp\/v2\/tags?post=1282"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}